initial commit
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitea/release_message.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								.gitea/release_message.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					previous_tag=$(git tag --sort=-creatordate | sed -n 2p)
 | 
				
			||||||
 | 
					git shortlog "${previous_tag}.." | sed 's/^./    &/'
 | 
				
			||||||
							
								
								
									
										36
									
								
								.gitea/rename_project.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										36
									
								
								.gitea/rename_project.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					while getopts a:n:u:d: flag
 | 
				
			||||||
 | 
					do
 | 
				
			||||||
 | 
					    case "${flag}" in
 | 
				
			||||||
 | 
					        a) author=${OPTARG};;
 | 
				
			||||||
 | 
					        n) name=${OPTARG};;
 | 
				
			||||||
 | 
					        u) urlname=${OPTARG};;
 | 
				
			||||||
 | 
					        d) description=${OPTARG};;
 | 
				
			||||||
 | 
					    esac
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo "Author: $author";
 | 
				
			||||||
 | 
					echo "Project Name: $name";
 | 
				
			||||||
 | 
					echo "Project URL name: $urlname";
 | 
				
			||||||
 | 
					echo "Description: $description";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo "Renaming project..."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					original_author="author_name"
 | 
				
			||||||
 | 
					original_name="project_name"
 | 
				
			||||||
 | 
					original_urlname="project_urlname"
 | 
				
			||||||
 | 
					original_description="project_description"
 | 
				
			||||||
 | 
					# for filename in $(find . -name "*.*") 
 | 
				
			||||||
 | 
					for filename in $(git ls-files) 
 | 
				
			||||||
 | 
					do
 | 
				
			||||||
 | 
					    sed -i "s/$original_author/$author/g" $filename
 | 
				
			||||||
 | 
					    sed -i "s/$original_name/$name/g" $filename
 | 
				
			||||||
 | 
					    sed -i "s/$original_urlname/$urlname/g" $filename
 | 
				
			||||||
 | 
					    sed -i "s/$original_description/$description/g" $filename
 | 
				
			||||||
 | 
					    echo "Renamed $filename"
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mv project_name $name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# This command runs only once on GHA!
 | 
				
			||||||
 | 
					rm -rf .gitea/template.yml
 | 
				
			||||||
							
								
								
									
										1
									
								
								.gitea/template.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitea/template.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					author: rochacbruno
 | 
				
			||||||
							
								
								
									
										92
									
								
								.gitea/workflows/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								.gitea/workflows/main.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
				
			|||||||
 | 
					# This is a basic workflow to help you get started with Actions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					name: CI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Controls when the workflow will run
 | 
				
			||||||
 | 
					on:
 | 
				
			||||||
 | 
					  # Triggers the workflow on push or pull request events but only for the main branch
 | 
				
			||||||
 | 
					  push:
 | 
				
			||||||
 | 
					    branches: [ main ]
 | 
				
			||||||
 | 
					  pull_request:
 | 
				
			||||||
 | 
					    branches: [ main ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Allows you to run this workflow manually from the Actions tab
 | 
				
			||||||
 | 
					  workflow_dispatch:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  linter:
 | 
				
			||||||
 | 
					    strategy:
 | 
				
			||||||
 | 
					      fail-fast: false
 | 
				
			||||||
 | 
					      matrix:
 | 
				
			||||||
 | 
					        python-version: [3.9]
 | 
				
			||||||
 | 
					        os: [ubuntu-latest]
 | 
				
			||||||
 | 
					    runs-on: ${{ matrix.os }}
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      - uses: actions/setup-python@v2
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          python-version: ${{ matrix.python-version }}
 | 
				
			||||||
 | 
					      - name: Install project
 | 
				
			||||||
 | 
					        run: make install
 | 
				
			||||||
 | 
					      - name: Run linter
 | 
				
			||||||
 | 
					        run: make lint
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  tests_linux:
 | 
				
			||||||
 | 
					    needs: linter
 | 
				
			||||||
 | 
					    strategy:
 | 
				
			||||||
 | 
					      fail-fast: false
 | 
				
			||||||
 | 
					      matrix:
 | 
				
			||||||
 | 
					        python-version: [3.9]
 | 
				
			||||||
 | 
					        os: [ubuntu-latest]
 | 
				
			||||||
 | 
					    runs-on: ${{ matrix.os }}
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					      - uses: actions/setup-python@v2
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          python-version: ${{ matrix.python-version }}
 | 
				
			||||||
 | 
					      - name: Install project
 | 
				
			||||||
 | 
					        run: make install
 | 
				
			||||||
 | 
					      - name: Run tests
 | 
				
			||||||
 | 
					        run: make test
 | 
				
			||||||
 | 
					      - name: "Upload coverage to Codecov"
 | 
				
			||||||
 | 
					        uses: codecov/codecov-action@v1
 | 
				
			||||||
 | 
					        # with:
 | 
				
			||||||
 | 
					        #   fail_ci_if_error: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # tests_mac:
 | 
				
			||||||
 | 
					  #   needs: linter
 | 
				
			||||||
 | 
					  #   strategy:
 | 
				
			||||||
 | 
					  #     fail-fast: false
 | 
				
			||||||
 | 
					  #     matrix:
 | 
				
			||||||
 | 
					  #       python-version: [3.9]
 | 
				
			||||||
 | 
					  #       os: [macos-latest]
 | 
				
			||||||
 | 
					  #   runs-on: ${{ matrix.os }}
 | 
				
			||||||
 | 
					  #   steps:
 | 
				
			||||||
 | 
					  #     - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					  #     - uses: actions/setup-python@v2
 | 
				
			||||||
 | 
					  #       with:
 | 
				
			||||||
 | 
					  #         python-version: ${{ matrix.python-version }}
 | 
				
			||||||
 | 
					  #     - name: Install project
 | 
				
			||||||
 | 
					  #       run: make install
 | 
				
			||||||
 | 
					  #     - name: Run tests
 | 
				
			||||||
 | 
					  #       run: make test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # tests_win:
 | 
				
			||||||
 | 
					  #   needs: linter
 | 
				
			||||||
 | 
					  #   strategy:
 | 
				
			||||||
 | 
					  #     fail-fast: false
 | 
				
			||||||
 | 
					  #     matrix:
 | 
				
			||||||
 | 
					  #       python-version: [3.9]
 | 
				
			||||||
 | 
					  #       os: [windows-latest]
 | 
				
			||||||
 | 
					  #   runs-on: ${{ matrix.os }}
 | 
				
			||||||
 | 
					  #   steps:
 | 
				
			||||||
 | 
					  #     - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					  #     - uses: actions/setup-python@v2
 | 
				
			||||||
 | 
					  #       with:
 | 
				
			||||||
 | 
					  #         python-version: ${{ matrix.python-version }}
 | 
				
			||||||
 | 
					  #     - name: Install Pip
 | 
				
			||||||
 | 
					  #       run: pip install --user --upgrade pip
 | 
				
			||||||
 | 
					  #     - name: Install project
 | 
				
			||||||
 | 
					  #       run: pip install -e .[test]
 | 
				
			||||||
 | 
					  #     - name: run tests
 | 
				
			||||||
 | 
					  #       run: pytest -s -vvvv -l --tb=long tests
 | 
				
			||||||
							
								
								
									
										48
									
								
								.gitea/workflows/release.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								.gitea/workflows/release.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					name: Upload Python Package
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					on:
 | 
				
			||||||
 | 
					  push:
 | 
				
			||||||
 | 
					    # Sequence of patterns matched against refs/tags
 | 
				
			||||||
 | 
					    tags:
 | 
				
			||||||
 | 
					      - '*' # Push events to matching v*, i.e. v1.0, v20.15.10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Allows you to run this workflow manually from the Actions tab
 | 
				
			||||||
 | 
					  workflow_dispatch:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  release:
 | 
				
			||||||
 | 
					    name: Create Release
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          # by default, it uses a depth of 1
 | 
				
			||||||
 | 
					          # this fetches all history so that we can read each commit
 | 
				
			||||||
 | 
					          fetch-depth: 0
 | 
				
			||||||
 | 
					      - name: Generate Changelog
 | 
				
			||||||
 | 
					        run: .gitea/release_message.sh > release_message.md
 | 
				
			||||||
 | 
					      - name: Release
 | 
				
			||||||
 | 
					        uses: softprops/action-gh-release@v1
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          body_path: release_message.md
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  deploy:
 | 
				
			||||||
 | 
					    needs: release
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					    - uses: actions/checkout@v1
 | 
				
			||||||
 | 
					    - name: Set up Python
 | 
				
			||||||
 | 
					      uses: actions/setup-python@v1
 | 
				
			||||||
 | 
					      with:
 | 
				
			||||||
 | 
					        python-version: '3.x'
 | 
				
			||||||
 | 
					    - name: Install dependencies
 | 
				
			||||||
 | 
					      run: |
 | 
				
			||||||
 | 
					        python -m pip install --upgrade pip
 | 
				
			||||||
 | 
					        pip install setuptools wheel twine
 | 
				
			||||||
 | 
					    - name: Build and publish
 | 
				
			||||||
 | 
					      env:
 | 
				
			||||||
 | 
					        TWINE_USERNAME: __token__
 | 
				
			||||||
 | 
					        TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
 | 
				
			||||||
 | 
					      run: |
 | 
				
			||||||
 | 
					        python setup.py sdist bdist_wheel
 | 
				
			||||||
 | 
					        twine upload dist/*
 | 
				
			||||||
							
								
								
									
										42
									
								
								.gitea/workflows/rename_project.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								.gitea/workflows/rename_project.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					name: Rename the project from template
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					on: [push]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					permissions: write-all
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					  rename-project:
 | 
				
			||||||
 | 
					    if: ${{ !endsWith (github.repository, 'Templates/Flask') }}
 | 
				
			||||||
 | 
					    runs-on: ubuntu-latest
 | 
				
			||||||
 | 
					    steps:
 | 
				
			||||||
 | 
					      - uses: actions/checkout@v2
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          # by default, it uses a depth of 1
 | 
				
			||||||
 | 
					          # this fetches all history so that we can read each commit
 | 
				
			||||||
 | 
					          fetch-depth: 0
 | 
				
			||||||
 | 
					          ref: ${{ github.head_ref }}
 | 
				
			||||||
 | 
					          
 | 
				
			||||||
 | 
					      - run: echo "REPOSITORY_NAME=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}' | tr '-' '_' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					        shell: bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - run: echo "REPOSITORY_URLNAME=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					        shell: bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - run: echo "REPOSITORY_OWNER=$(echo '${{ github.repository }}' | awk -F '/' '{print $1}')" >> $GITHUB_ENV
 | 
				
			||||||
 | 
					        shell: bash
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					      - name: Is this still a template
 | 
				
			||||||
 | 
					        id: is_template
 | 
				
			||||||
 | 
					        run: echo "::set-output name=is_template::$(ls .gitea/template.yml &> /dev/null && echo true || echo false)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      - name: Rename the project
 | 
				
			||||||
 | 
					        if: steps.is_template.outputs.is_template == 'true'
 | 
				
			||||||
 | 
					        run: |
 | 
				
			||||||
 | 
					          echo "Renaming the project with -a(author) ${{ env.REPOSITORY_OWNER }} -n(name) ${{ env.REPOSITORY_NAME }} -u(urlname) ${{ env.REPOSITORY_URLNAME }}"
 | 
				
			||||||
 | 
					          .gitea/rename_project.sh -a ${{ env.REPOSITORY_OWNER }} -n ${{ env.REPOSITORY_NAME }} -u ${{ env.REPOSITORY_URLNAME }} -d "Awesome ${{ env.REPOSITORY_NAME }} created by ${{ env.REPOSITORY_OWNER }}"
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      - uses: stefanzweifel/git-auto-commit-action@v4
 | 
				
			||||||
 | 
					        with:
 | 
				
			||||||
 | 
					          commit_message: "✅ Ready to clone and code."
 | 
				
			||||||
 | 
					          # commit_options: '--amend --no-edit'
 | 
				
			||||||
 | 
					          push_options: --force
 | 
				
			||||||
							
								
								
									
										133
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,133 @@
 | 
				
			|||||||
 | 
					# Byte-compiled / optimized / DLL files
 | 
				
			||||||
 | 
					__pycache__/
 | 
				
			||||||
 | 
					*.py[cod]
 | 
				
			||||||
 | 
					*$py.class
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# C extensions
 | 
				
			||||||
 | 
					*.so
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Distribution / packaging
 | 
				
			||||||
 | 
					.Python
 | 
				
			||||||
 | 
					build/
 | 
				
			||||||
 | 
					develop-eggs/
 | 
				
			||||||
 | 
					dist/
 | 
				
			||||||
 | 
					downloads/
 | 
				
			||||||
 | 
					eggs/
 | 
				
			||||||
 | 
					.eggs/
 | 
				
			||||||
 | 
					lib/
 | 
				
			||||||
 | 
					lib64/
 | 
				
			||||||
 | 
					parts/
 | 
				
			||||||
 | 
					sdist/
 | 
				
			||||||
 | 
					var/
 | 
				
			||||||
 | 
					wheels/
 | 
				
			||||||
 | 
					pip-wheel-metadata/
 | 
				
			||||||
 | 
					share/python-wheels/
 | 
				
			||||||
 | 
					*.egg-info/
 | 
				
			||||||
 | 
					.installed.cfg
 | 
				
			||||||
 | 
					*.egg
 | 
				
			||||||
 | 
					MANIFEST
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PyInstaller
 | 
				
			||||||
 | 
					#  Usually these files are written by a python script from a template
 | 
				
			||||||
 | 
					#  before PyInstaller builds the exe, so as to inject date/other infos into it.
 | 
				
			||||||
 | 
					*.manifest
 | 
				
			||||||
 | 
					*.spec
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Installer logs
 | 
				
			||||||
 | 
					pip-log.txt
 | 
				
			||||||
 | 
					pip-delete-this-directory.txt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Unit test / coverage reports
 | 
				
			||||||
 | 
					htmlcov/
 | 
				
			||||||
 | 
					.tox/
 | 
				
			||||||
 | 
					.nox/
 | 
				
			||||||
 | 
					.coverage
 | 
				
			||||||
 | 
					.coverage.*
 | 
				
			||||||
 | 
					.cache
 | 
				
			||||||
 | 
					nosetests.xml
 | 
				
			||||||
 | 
					coverage.xml
 | 
				
			||||||
 | 
					*.cover
 | 
				
			||||||
 | 
					*.py,cover
 | 
				
			||||||
 | 
					.hypothesis/
 | 
				
			||||||
 | 
					.pytest_cache/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Translations
 | 
				
			||||||
 | 
					*.mo
 | 
				
			||||||
 | 
					*.pot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Django stuff:
 | 
				
			||||||
 | 
					*.log
 | 
				
			||||||
 | 
					local_settings.py
 | 
				
			||||||
 | 
					db.sqlite3
 | 
				
			||||||
 | 
					db.sqlite3-journal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Flask stuff:
 | 
				
			||||||
 | 
					instance/
 | 
				
			||||||
 | 
					.webassets-cache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Scrapy stuff:
 | 
				
			||||||
 | 
					.scrapy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Sphinx documentation
 | 
				
			||||||
 | 
					docs/_build/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PyBuilder
 | 
				
			||||||
 | 
					target/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Jupyter Notebook
 | 
				
			||||||
 | 
					.ipynb_checkpoints
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# IPython
 | 
				
			||||||
 | 
					profile_default/
 | 
				
			||||||
 | 
					ipython_config.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# pyenv
 | 
				
			||||||
 | 
					.python-version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# pipenv
 | 
				
			||||||
 | 
					#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
 | 
				
			||||||
 | 
					#   However, in case of collaboration, if having platform-specific dependencies or dependencies
 | 
				
			||||||
 | 
					#   having no cross-platform support, pipenv may install dependencies that don't work, or not
 | 
				
			||||||
 | 
					#   install all needed dependencies.
 | 
				
			||||||
 | 
					#Pipfile.lock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# PEP 582; used by e.g. github.com/David-OConnor/pyflow
 | 
				
			||||||
 | 
					__pypackages__/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Celery stuff
 | 
				
			||||||
 | 
					celerybeat-schedule
 | 
				
			||||||
 | 
					celerybeat.pid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# SageMath parsed files
 | 
				
			||||||
 | 
					*.sage.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Environments
 | 
				
			||||||
 | 
					.env
 | 
				
			||||||
 | 
					.venv
 | 
				
			||||||
 | 
					env/
 | 
				
			||||||
 | 
					venv/
 | 
				
			||||||
 | 
					ENV/
 | 
				
			||||||
 | 
					env.bak/
 | 
				
			||||||
 | 
					venv.bak/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Spyder project settings
 | 
				
			||||||
 | 
					.spyderproject
 | 
				
			||||||
 | 
					.spyproject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Rope project settings
 | 
				
			||||||
 | 
					.ropeproject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# mkdocs documentation
 | 
				
			||||||
 | 
					/site
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# mypy
 | 
				
			||||||
 | 
					.mypy_cache/
 | 
				
			||||||
 | 
					.dmypy.json
 | 
				
			||||||
 | 
					dmypy.json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Pyre type checker
 | 
				
			||||||
 | 
					.pyre/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# templates
 | 
				
			||||||
 | 
					.gitea/templates/*
 | 
				
			||||||
 | 
					development.db
 | 
				
			||||||
							
								
								
									
										195
									
								
								ABOUT_THIS_TEMPLATE.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								ABOUT_THIS_TEMPLATE.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,195 @@
 | 
				
			|||||||
 | 
					# About this template
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Hi, I created this template to help you get started with a new project.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I have created and maintained a number of python libraries, applications and 
 | 
				
			||||||
 | 
					frameworks and during those years I have learned a lot about how to create a 
 | 
				
			||||||
 | 
					project structure and how to structure a project to be as modular and simple 
 | 
				
			||||||
 | 
					as possible.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Some decisions I have made while creating this template are:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 - Create a project structure that is as modular as possible.
 | 
				
			||||||
 | 
					 - Keep it simple and easy to maintain.
 | 
				
			||||||
 | 
					 - Allow for a lot of flexibility and customizability.
 | 
				
			||||||
 | 
					 - Low dependency (this template doesn't add dependencies)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Structure
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Lets take a look at the structure of this template:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```text
 | 
				
			||||||
 | 
					├── Containerfile            # The file to build a container using buildah or docker
 | 
				
			||||||
 | 
					├── CONTRIBUTING.md          # Onboarding instructions for new contributors
 | 
				
			||||||
 | 
					├── docs                     # Documentation site (add more .md files here)
 | 
				
			||||||
 | 
					│   └── index.md             # The index page for the docs site
 | 
				
			||||||
 | 
					├── .gitea                   # Gitea metadata for repository
 | 
				
			||||||
 | 
					│   ├── release_message.sh   # A script to generate a release message
 | 
				
			||||||
 | 
					│   └── workflows            # The CI pipeline for Gitea Actions
 | 
				
			||||||
 | 
					├── .gitignore               # A list of files to ignore when pushing to Gitea
 | 
				
			||||||
 | 
					├── HISTORY.md               # Auto generated list of changes to the project
 | 
				
			||||||
 | 
					├── LICENSE                  # The license for the project
 | 
				
			||||||
 | 
					├── Makefile                 # A collection of utilities to manage the project
 | 
				
			||||||
 | 
					├── MANIFEST.in              # A list of files to include in a package
 | 
				
			||||||
 | 
					├── mkdocs.yml               # Configuration for documentation site
 | 
				
			||||||
 | 
					├── project_name             # The main python package for the project
 | 
				
			||||||
 | 
					│   ├── base.py              # The base module for the project
 | 
				
			||||||
 | 
					│   ├── __init__.py          # This tells Python that this is a package
 | 
				
			||||||
 | 
					│   ├── __main__.py          # The entry point for the project
 | 
				
			||||||
 | 
					│   └── VERSION              # The version for the project is kept in a static file
 | 
				
			||||||
 | 
					├── README.md                # The main readme for the project
 | 
				
			||||||
 | 
					├── setup.py                 # The setup.py file for installing and packaging the project
 | 
				
			||||||
 | 
					├── requirements.txt         # An empty file to hold the requirements for the project
 | 
				
			||||||
 | 
					├── requirements-test.txt    # List of requirements for testing and devlopment
 | 
				
			||||||
 | 
					├── setup.py                 # The setup.py file for installing and packaging the project
 | 
				
			||||||
 | 
					└── tests                    # Unit tests for the project (add mote tests files here)
 | 
				
			||||||
 | 
					    ├── conftest.py          # Configuration, hooks and fixtures for pytest
 | 
				
			||||||
 | 
					    ├── __init__.py          # This tells Python that this is a test package
 | 
				
			||||||
 | 
					    └── test_base.py         # The base test case for the project
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## FAQ
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Frequent asked questions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Why this template is not using [Poetry](https://python-poetry.org/) ?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I really like Poetry and I think it is a great tool to manage your python projects,
 | 
				
			||||||
 | 
					if you want to switch to poetry, you can run `make switch-to-poetry`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					But for this template I wanted to keep it simple.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Setuptools is the most simple and well supported way of packaging a Python project,
 | 
				
			||||||
 | 
					it doesn't require extra dependencies and is the easiest way to install the project.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Also, poetry doesn't have a good support for installing projects in development mode yet.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Why the `requirements.txt` is empty ?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This template is a low dependency project, so it doesn't have any extra dependencies.
 | 
				
			||||||
 | 
					You can add new dependencies as you will or you can use the `make init` command to
 | 
				
			||||||
 | 
					generate a `requirements.txt` file based on the template you choose `flask, fastapi, click etc`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Why there is a `requirements-test.txt` file ?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This file lists all the requirements for testing and development,
 | 
				
			||||||
 | 
					I think the development environment and testing environment should be as similar as possible.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Except those tools that are up to the developer choice (like ipython, ipdb etc).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Why the template doesn't have a `pyproject.toml` file ?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It is possible to run `pip install https://git.disi.dev/name/repo/tarball/main` and
 | 
				
			||||||
 | 
					have pip to download the package direcly from Git repo.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For that to work you need to have a `setup.py` file, and `pyproject.toml` is not
 | 
				
			||||||
 | 
					supported for that kind of installation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I think it is easier for example you want to install specific branch or tag you can
 | 
				
			||||||
 | 
					do `pip install https://git.disi.dev/name/repo/tarball/{TAG|REVISON|COMMIT}`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					People automating CI for your project will be grateful for having a setup.py file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Why isn't this template made as a cookiecutter template?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I really like [cookiecutter](https://github.com/cookiecutter/cookiecutter) and it is a great way to create new projects,
 | 
				
			||||||
 | 
					to use this template doesn't require to install extra tooling such as cookiecutter.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The substituions are done using gitea actions and a simple sed script.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Why `VERSION` is kept in a static plain text file?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I used to have my version inside my main module in a `__version__` variable, then
 | 
				
			||||||
 | 
					I had to do some tricks to read that version variable inside the setuptools 
 | 
				
			||||||
 | 
					`setup.py` file because that would be available only after the installation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I decided to keep the version in a static file because it is easier to read from
 | 
				
			||||||
 | 
					wherever I want without the need to install the package.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					e.g: `cat project_name/VERSION` will get the project version without harming
 | 
				
			||||||
 | 
					with module imports or anything else, it is useful for CI, logs and debugging.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Why to include `tests`, `history` and `Containerfile` as part of the release?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The `MANIFEST.in` file is used to include the files in the release, once the 
 | 
				
			||||||
 | 
					project is released to PyPI all the files listed on MANIFEST.in will be included
 | 
				
			||||||
 | 
					even if the files are static or not related to Python.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Some build systems such as RPM, DEB, AUR for some Linux distributions, and also
 | 
				
			||||||
 | 
					internal repackaging systems tends to run the tests before the packaging is performed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The Containerfile can be useful to provide a safer execution environment for 
 | 
				
			||||||
 | 
					the project when running on a testing environment.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I added those files to make it easier for packaging in different formats.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Why conftest includes a go_to_tmpdir fixture?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When your project deals with file system operations, it is a good idea to use
 | 
				
			||||||
 | 
					a fixture to create a temporary directory and then remove it after the test.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Before executing each test pytest will create a temporary directory and will
 | 
				
			||||||
 | 
					change the working directory to that path and run the test.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					So the test can create temporary artifacts isolated from other tests.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					After the execution Pytest will remove the temporary directory.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Why this template is not using [pre-commit](https://pre-commit.com/) ?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pre-commit is an excellent tool to automate checks and formatting on your code.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					However I figured out that pre-commit adds extra dependency and it an entry barrier
 | 
				
			||||||
 | 
					for new contributors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Having the linting, checks and formatting as simple commands on the [Makefile](Makefile)
 | 
				
			||||||
 | 
					makes it easier to undestand and change.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Once the project is bigger and complex, having pre-commit as a dependency can be a good idea.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Why the CLI is not using click?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I wanted to provide a simple template for a CLI application on the project main entry point
 | 
				
			||||||
 | 
					click and typer are great alternatives but are external dependencies and this template
 | 
				
			||||||
 | 
					doesn't add dependencies besides those used for development.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Why this doesn't provide a full example of application using Flask or Django?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					as I said before, I want it to be simple and multipurpose, so I decided to not include
 | 
				
			||||||
 | 
					external dependencies and programming design decisions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It is up to you to decide if you want to use Flask or Django and to create your application
 | 
				
			||||||
 | 
					the way you think is best.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This template provides utilities in the Makefile to make it easier to you can run:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					$ make init 
 | 
				
			||||||
 | 
					Which template do you want to apply? [flask, fastapi, click, typer]? > flask
 | 
				
			||||||
 | 
					Generating a new project with Flask ...
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Then the above will download the Flask template and apply it to the project.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## The Makefile
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					All the utilities for the template and project are on the Makefile
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					❯ make
 | 
				
			||||||
 | 
					Usage: make <target>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Targets:
 | 
				
			||||||
 | 
					help:             ## Show the help.
 | 
				
			||||||
 | 
					install:          ## Install the project in dev mode.
 | 
				
			||||||
 | 
					fmt:              ## Format code using black & isort.
 | 
				
			||||||
 | 
					lint:             ## Run pep8, black, mypy linters.
 | 
				
			||||||
 | 
					test: lint        ## Run tests and generate coverage report.
 | 
				
			||||||
 | 
					watch:            ## Run tests on every change.
 | 
				
			||||||
 | 
					clean:            ## Clean unused files.
 | 
				
			||||||
 | 
					virtualenv:       ## Create a virtual environment.
 | 
				
			||||||
 | 
					release:          ## Create a new tag for release.
 | 
				
			||||||
 | 
					docs:             ## Build the documentation.
 | 
				
			||||||
 | 
					switch-to-poetry: ## Switch to poetry package manager.
 | 
				
			||||||
 | 
					init:             ## Initialize the project based on an application template.
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										113
									
								
								CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								CONTRIBUTING.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,113 @@
 | 
				
			|||||||
 | 
					# How to develop on this project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					project_name welcomes contributions from the community.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					**You need PYTHON3!**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This instructions are for linux base systems. (Linux, MacOS, BSD, etc.)
 | 
				
			||||||
 | 
					## Setting up your own fork of this repo.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- On gitea interface click on `Fork` button.
 | 
				
			||||||
 | 
					- Clone your fork of this repo. `git clone git@git.disi.dev:YOUR_GIT_USERNAME/project_urlname.git`
 | 
				
			||||||
 | 
					- Enter the directory `cd project_urlname`
 | 
				
			||||||
 | 
					- Add upstream repo `git remote add upstream https://git.disi.dev/author_name/project_urlname`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Setting up your own virtual environment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Run `make virtualenv` to create a virtual environment.
 | 
				
			||||||
 | 
					then activate it with `source .venv/bin/activate`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Install the project in develop mode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Run `make install` to install the project in develop mode.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Run the tests to ensure everything is working
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Run `make test` to run the tests.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Create a new branch to work on your contribution
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Run `git checkout -b my_contribution`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Make your changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Edit the files using your preferred editor. (we recommend VIM or VSCode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Format the code
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Run `make fmt` to format the code.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Run the linter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Run `make lint` to run the linter.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Test your changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Run `make test` to run the tests.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ensure code coverage report shows `100%` coverage, add tests to your PR.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Build the docs locally
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Run `make docs` to build the docs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Ensure your new changes are documented.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Commit your changes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This project uses [conventional git commit messages](https://www.conventionalcommits.org/en/v1.0.0/).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example: `fix(package): update setup.py arguments 🎉` (emojis are fine too)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Push your changes to your fork
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Run `git push origin my_contribution`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Submit a pull request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					On gitea interface, click on `Pull Request` button.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Wait CI to run and one of the developers will review your PR.
 | 
				
			||||||
 | 
					## Makefile utilities
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This project comes with a `Makefile` that contains a number of useful utility.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash 
 | 
				
			||||||
 | 
					❯ make
 | 
				
			||||||
 | 
					Usage: make <target>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Targets:
 | 
				
			||||||
 | 
					help:             ## Show the help.
 | 
				
			||||||
 | 
					install:          ## Install the project in dev mode.
 | 
				
			||||||
 | 
					fmt:              ## Format code using black & isort.
 | 
				
			||||||
 | 
					lint:             ## Run pep8, black, mypy linters.
 | 
				
			||||||
 | 
					test: lint        ## Run tests and generate coverage report.
 | 
				
			||||||
 | 
					watch:            ## Run tests on every change.
 | 
				
			||||||
 | 
					clean:            ## Clean unused files.
 | 
				
			||||||
 | 
					virtualenv:       ## Create a virtual environment.
 | 
				
			||||||
 | 
					release:          ## Create a new tag for release.
 | 
				
			||||||
 | 
					docs:             ## Build the documentation.
 | 
				
			||||||
 | 
					switch-to-poetry: ## Switch to poetry package manager.
 | 
				
			||||||
 | 
					init:             ## Initialize the project based on an application template.
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Making a new release
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This project uses [semantic versioning](https://semver.org/) and tags releases with `X.Y.Z`
 | 
				
			||||||
 | 
					Every time a new tag is created and pushed to the remote repo, gitea actions will
 | 
				
			||||||
 | 
					automatically create a new release on gitea and trigger a release on PyPI.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For this to work you need to setup a secret called `PIPY_API_TOKEN` on the project settings>secrets, 
 | 
				
			||||||
 | 
					this token can be generated on [pypi.org](https://pypi.org/account/).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To trigger a new release all you need to do is.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. If you have changes to add to the repo
 | 
				
			||||||
 | 
					    * Make your changes following the steps described above.
 | 
				
			||||||
 | 
					    * Commit your changes following the [conventional git commit messages](https://www.conventionalcommits.org/en/v1.0.0/).
 | 
				
			||||||
 | 
					2. Run the tests to ensure everything is working.
 | 
				
			||||||
 | 
					4. Run `make release` to create a new tag and push it to the remote repo.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					the `make release` will ask you the version number to create the tag, ex: type `0.1.1` when you are asked.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					> **CAUTION**:  The make release will change local changelog files and commit all the unstaged changes you have.
 | 
				
			||||||
							
								
								
									
										9
									
								
								Containerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								Containerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					FROM python:3.7-alpine
 | 
				
			||||||
 | 
					COPY . /app
 | 
				
			||||||
 | 
					WORKDIR /app
 | 
				
			||||||
 | 
					RUN pip install .
 | 
				
			||||||
 | 
					RUN project_name create-db
 | 
				
			||||||
 | 
					RUN project_name populate-db
 | 
				
			||||||
 | 
					RUN project_name add-user -u admin -p admin
 | 
				
			||||||
 | 
					EXPOSE 5000
 | 
				
			||||||
 | 
					CMD ["project_name", "run"]
 | 
				
			||||||
							
								
								
									
										0
									
								
								HISTORY.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								HISTORY.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										24
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					This is free and unencumbered software released into the public domain.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Anyone is free to copy, modify, publish, use, compile, sell, or
 | 
				
			||||||
 | 
					distribute this software, either in source code form or as a compiled
 | 
				
			||||||
 | 
					binary, for any purpose, commercial or non-commercial, and by any
 | 
				
			||||||
 | 
					means.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In jurisdictions that recognize copyright laws, the author or authors
 | 
				
			||||||
 | 
					of this software dedicate any and all copyright interest in the
 | 
				
			||||||
 | 
					software to the public domain. We make this dedication for the benefit
 | 
				
			||||||
 | 
					of the public at large and to the detriment of our heirs and
 | 
				
			||||||
 | 
					successors. We intend this dedication to be an overt act of
 | 
				
			||||||
 | 
					relinquishment in perpetuity of all present and future rights to this
 | 
				
			||||||
 | 
					software under copyright law.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | 
				
			||||||
 | 
					EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | 
				
			||||||
 | 
					MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 | 
				
			||||||
 | 
					IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 | 
				
			||||||
 | 
					OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 | 
				
			||||||
 | 
					ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 | 
				
			||||||
 | 
					OTHER DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For more information, please refer to <https://unlicense.org>
 | 
				
			||||||
							
								
								
									
										5
									
								
								MANIFEST.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								MANIFEST.in
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					include LICENSE
 | 
				
			||||||
 | 
					include HISTORY.md
 | 
				
			||||||
 | 
					include Containerfile
 | 
				
			||||||
 | 
					graft tests
 | 
				
			||||||
 | 
					graft project_name
 | 
				
			||||||
							
								
								
									
										119
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,119 @@
 | 
				
			|||||||
 | 
					.ONESHELL:
 | 
				
			||||||
 | 
					ENV_PREFIX=$(shell python -c "if __import__('pathlib').Path('.venv/bin/pip').exists(): print('.venv/bin/')")
 | 
				
			||||||
 | 
					USING_POETRY=$(shell grep "tool.poetry" pyproject.toml && echo "yes")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: help
 | 
				
			||||||
 | 
					help:             ## Show the help.
 | 
				
			||||||
 | 
						@echo "Usage: make <target>"
 | 
				
			||||||
 | 
						@echo ""
 | 
				
			||||||
 | 
						@echo "Targets:"
 | 
				
			||||||
 | 
						@fgrep "##" Makefile | fgrep -v fgrep
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: show
 | 
				
			||||||
 | 
					show:             ## Show the current environment.
 | 
				
			||||||
 | 
						@echo "Current environment:"
 | 
				
			||||||
 | 
						@if [ "$(USING_POETRY)" ]; then poetry env info && exit; fi
 | 
				
			||||||
 | 
						@echo "Running using $(ENV_PREFIX)"
 | 
				
			||||||
 | 
						@$(ENV_PREFIX)python -V
 | 
				
			||||||
 | 
						@$(ENV_PREFIX)python -m site
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: install
 | 
				
			||||||
 | 
					install:          ## Install the project in dev mode.
 | 
				
			||||||
 | 
						@if [ "$(USING_POETRY)" ]; then poetry install && exit; fi
 | 
				
			||||||
 | 
						@echo "Don't forget to run 'make virtualenv' if you got errors."
 | 
				
			||||||
 | 
						$(ENV_PREFIX)pip install -e .[test]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: fmt
 | 
				
			||||||
 | 
					fmt:              ## Format code using black & isort.
 | 
				
			||||||
 | 
						$(ENV_PREFIX)isort project_name/
 | 
				
			||||||
 | 
						$(ENV_PREFIX)black -l 79 project_name/
 | 
				
			||||||
 | 
						$(ENV_PREFIX)black -l 79 tests/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: lint
 | 
				
			||||||
 | 
					lint:             ## Run pep8, black, mypy linters.
 | 
				
			||||||
 | 
						$(ENV_PREFIX)flake8 project_name/
 | 
				
			||||||
 | 
						$(ENV_PREFIX)black -l 79 --check project_name/
 | 
				
			||||||
 | 
						$(ENV_PREFIX)black -l 79 --check tests/
 | 
				
			||||||
 | 
						$(ENV_PREFIX)mypy --ignore-missing-imports project_name/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: test
 | 
				
			||||||
 | 
					test: lint        ## Run tests and generate coverage report.
 | 
				
			||||||
 | 
						$(ENV_PREFIX)pytest -v --cov-config .coveragerc --cov=project_name -l --tb=short --maxfail=1 tests/
 | 
				
			||||||
 | 
						$(ENV_PREFIX)coverage xml
 | 
				
			||||||
 | 
						$(ENV_PREFIX)coverage html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: watch
 | 
				
			||||||
 | 
					watch:            ## Run tests on every change.
 | 
				
			||||||
 | 
						ls **/**.py | entr $(ENV_PREFIX)pytest -s -vvv -l --tb=long --maxfail=1 tests/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: clean
 | 
				
			||||||
 | 
					clean:            ## Clean unused files.
 | 
				
			||||||
 | 
						@find ./ -name '*.pyc' -exec rm -f {} \;
 | 
				
			||||||
 | 
						@find ./ -name '__pycache__' -exec rm -rf {} \;
 | 
				
			||||||
 | 
						@find ./ -name 'Thumbs.db' -exec rm -f {} \;
 | 
				
			||||||
 | 
						@find ./ -name '*~' -exec rm -f {} \;
 | 
				
			||||||
 | 
						@rm -rf .cache
 | 
				
			||||||
 | 
						@rm -rf .pytest_cache
 | 
				
			||||||
 | 
						@rm -rf .mypy_cache
 | 
				
			||||||
 | 
						@rm -rf build
 | 
				
			||||||
 | 
						@rm -rf dist
 | 
				
			||||||
 | 
						@rm -rf *.egg-info
 | 
				
			||||||
 | 
						@rm -rf htmlcov
 | 
				
			||||||
 | 
						@rm -rf .tox/
 | 
				
			||||||
 | 
						@rm -rf docs/_build
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: virtualenv
 | 
				
			||||||
 | 
					virtualenv:       ## Create a virtual environment.
 | 
				
			||||||
 | 
						@if [ "$(USING_POETRY)" ]; then poetry install && exit; fi
 | 
				
			||||||
 | 
						@echo "creating virtualenv ..."
 | 
				
			||||||
 | 
						@rm -rf .venv
 | 
				
			||||||
 | 
						@python3 -m venv .venv
 | 
				
			||||||
 | 
						@./.venv/bin/pip install -U pip
 | 
				
			||||||
 | 
						@./.venv/bin/pip install -e .[test]
 | 
				
			||||||
 | 
						@echo
 | 
				
			||||||
 | 
						@echo "!!! Please run 'source .venv/bin/activate' to enable the environment !!!"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: release
 | 
				
			||||||
 | 
					release:          ## Create a new tag for release.
 | 
				
			||||||
 | 
						@echo "WARNING: This operation will create s version tag and push to gitea"
 | 
				
			||||||
 | 
						@read -p "Version? (provide the next x.y.z semver) : " TAG
 | 
				
			||||||
 | 
						@echo "creating git tag : $${TAG}"
 | 
				
			||||||
 | 
						@git tag $${TAG}
 | 
				
			||||||
 | 
						@echo "$${TAG}" > project_name/VERSION
 | 
				
			||||||
 | 
						@$(ENV_PREFIX)gitchangelog > HISTORY.md
 | 
				
			||||||
 | 
						@git add project_name/VERSION HISTORY.md
 | 
				
			||||||
 | 
						@git commit -m "release: version $${TAG} 🚀"
 | 
				
			||||||
 | 
						@git push -u origin HEAD --tags
 | 
				
			||||||
 | 
						@echo "Gitea Actions will detect the new tag and release the new version."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: docs
 | 
				
			||||||
 | 
					docs:             ## Build the documentation.
 | 
				
			||||||
 | 
						@echo "building documentation ..."
 | 
				
			||||||
 | 
						@$(ENV_PREFIX)mkdocs build
 | 
				
			||||||
 | 
						URL="site/index.html"; xdg-open $$URL || sensible-browser $$URL || x-www-browser $$URL || gnome-open $$URL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.PHONY: switch-to-poetry
 | 
				
			||||||
 | 
					switch-to-poetry: ## Switch to poetry package manager.
 | 
				
			||||||
 | 
						@echo "Switching to poetry ..."
 | 
				
			||||||
 | 
						@if ! poetry --version > /dev/null; then echo 'poetry is required, install from https://python-poetry.org/'; exit 1; fi
 | 
				
			||||||
 | 
						@rm -rf .venv
 | 
				
			||||||
 | 
						@poetry init --no-interaction --name=a_flask_test --author=rochacbruno
 | 
				
			||||||
 | 
						@echo "" >> pyproject.toml
 | 
				
			||||||
 | 
						@echo "[tool.poetry.scripts]" >> pyproject.toml
 | 
				
			||||||
 | 
						@echo "project_name = 'project_name.__main__:main'" >> pyproject.toml 
 | 
				
			||||||
 | 
						@cat requirements.txt | while read in; do poetry add --no-interaction "$${in}"; done
 | 
				
			||||||
 | 
						@cat requirements-base.txt | while read in; do poetry add --no-interaction "$${in}" --dev; done
 | 
				
			||||||
 | 
						@cat requirements-test.txt | while read in; do poetry add --no-interaction "$${in}" --dev; done
 | 
				
			||||||
 | 
						@poetry install --no-interaction
 | 
				
			||||||
 | 
						@mkdir -p .gitea/backup
 | 
				
			||||||
 | 
						@mv requirements* .gitea/backup
 | 
				
			||||||
 | 
						@mv setup.py .gitea/backup
 | 
				
			||||||
 | 
						@echo "You have switched to https://python-poetry.org/ package manager."
 | 
				
			||||||
 | 
						@echo "Please run 'poetry shell' or 'poetry run project_name'"
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					# This project has been generated from rochacbruno/flask-project-template
 | 
				
			||||||
 | 
					# __author__ = 'rochacbruno'
 | 
				
			||||||
 | 
					# __repo__ = https://github.com/rochacbruno/flask-project-template
 | 
				
			||||||
 | 
					# __sponsor__ = https://github.com/sponsors/rochacbruno/
 | 
				
			||||||
							
								
								
									
										101
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,101 @@
 | 
				
			|||||||
 | 
					# Flask Project Template
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A full feature Flask project template.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					See also 
 | 
				
			||||||
 | 
					- [Python-Project-Template](https://git.disi.dev/Templates/Python/) for a lean, low dependency Python app.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### HOW TO USE THIS TEMPLATE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Create a new repository from this template and choose a name for your project
 | 
				
			||||||
 | 
					   (e.g. `my_awesome_project` - recommendation is to use all lowercase and underscores separation for repo names.)
 | 
				
			||||||
 | 
					2. Wait until the first run of CI finishes (Gitea Actions will process the template and commit to your new repo)
 | 
				
			||||||
 | 
					3. If you want Automatic Release to [PyPI](https://pypi.org)  
 | 
				
			||||||
 | 
					  On the new repository `settings->secrets` add your `PYPI_API_TOKEN` (get the tokens on PyPI website)
 | 
				
			||||||
 | 
					4. Read the file [CONTRIBUTING.md](CONTRIBUTING.md)
 | 
				
			||||||
 | 
					5. Then clone your new project and happy coding!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					> **NOTE**: **WAIT** until first CI run on gitea actions before cloning your new project.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### What is included on this template?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- 🍾 A full feature Flask application with CLI, API, Admin interface, web UI and modular configuration.
 | 
				
			||||||
 | 
					- 📦 A basic [setup.py](setup.py) file to provide installation, packaging and distribution for your project.  
 | 
				
			||||||
 | 
					  Template uses setuptools because it's the de-facto standard for Python packages, you can run `make switch-to-poetry` later if you want.
 | 
				
			||||||
 | 
					- 🤖 A [Makefile](Makefile) with the most useful commands to install, test, lint, format and release your project.
 | 
				
			||||||
 | 
					- 📃 Documentation structure using [mkdocs](http://www.mkdocs.org)
 | 
				
			||||||
 | 
					- 💬 Auto generation of change log using **gitchangelog** to keep a HISTORY.md file automatically based on your commit history on every release.
 | 
				
			||||||
 | 
					- 🐋 A simple [Containerfile](Containerfile) to build a container image for your project.  
 | 
				
			||||||
 | 
					  `Containerfile` is a more open standard for building container images than Dockerfile, you can use buildah or docker with this file.
 | 
				
			||||||
 | 
					- 🧪 Testing structure using [pytest](https://docs.pytest.org/en/latest/)
 | 
				
			||||||
 | 
					- ✅ Code linting using [flake8](https://flake8.pycqa.org/en/latest/)
 | 
				
			||||||
 | 
					- 📊 Code coverage reports using [codecov](https://about.codecov.io/sign-up/)
 | 
				
			||||||
 | 
					- 🛳️ Automatic release to [PyPI](https://pypi.org) using [twine](https://twine.readthedocs.io/en/latest/) and gitea actions.
 | 
				
			||||||
 | 
					- 🎯 Entry points to execute your program using `python -m <project_name>` or `$ project_name` with basic CLI argument parsing.
 | 
				
			||||||
 | 
					- 🔄 Continuous integration using [Gitea Actions](.gitea/workflows/) with jobs to lint, test and release your project on Linux, Mac and Windows environments.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					> Curious about architectural decisions on this template? read [ABOUT_THIS_TEMPLATE.md](ABOUT_THIS_TEMPLATE.md)  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<!--  DELETE THE LINES ABOVE THIS AND WRITE YOUR PROJECT README BELOW -->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					# project_name Flask Application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					project_description
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Installation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					From source:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					git clone https://git.disi.dev/author_name/project_urlname project_name
 | 
				
			||||||
 | 
					cd project_name
 | 
				
			||||||
 | 
					make install
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					From pypi:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					pip install project_name
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Executing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This application has a CLI interface that extends the Flask CLI.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Just run:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					$ project_name
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					or
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					$ python -m project_name
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To see the help message and usage instructions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## First run
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					project_name create-db   # run once
 | 
				
			||||||
 | 
					project_name populate-db  # run once (optional)
 | 
				
			||||||
 | 
					project_name add-user -u admin -p 1234  # ads a user
 | 
				
			||||||
 | 
					project_name run
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Go to:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Website: http://localhost:5000
 | 
				
			||||||
 | 
					- Admin: http://localhost:5000/admin/
 | 
				
			||||||
 | 
					  - user: admin, senha: 1234
 | 
				
			||||||
 | 
					- API GET:
 | 
				
			||||||
 | 
					  - http://localhost:5000/api/v1/product/
 | 
				
			||||||
 | 
					  - http://localhost:5000/api/v1/product/1
 | 
				
			||||||
 | 
					  - http://localhost:5000/api/v1/product/2
 | 
				
			||||||
 | 
					  - http://localhost:5000/api/v1/product/3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					> **Note**: You can also use `flask run` to run the application.
 | 
				
			||||||
							
								
								
									
										77
									
								
								apply.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										77
									
								
								apply.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,77 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					while getopts a:n:u:d: flag
 | 
				
			||||||
 | 
					do
 | 
				
			||||||
 | 
					    case "${flag}" in
 | 
				
			||||||
 | 
					        a) author=${OPTARG};;
 | 
				
			||||||
 | 
					        n) name=${OPTARG};;
 | 
				
			||||||
 | 
					        u) urlname=${OPTARG};;
 | 
				
			||||||
 | 
					        d) description=${OPTARG};;
 | 
				
			||||||
 | 
					    esac
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo "Author: $author";
 | 
				
			||||||
 | 
					echo "Project Name: $name";
 | 
				
			||||||
 | 
					echo "Project URL name: $urlname";
 | 
				
			||||||
 | 
					echo "Description: $description";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo "Rendering the Flask template..."
 | 
				
			||||||
 | 
					original_author="author_name"
 | 
				
			||||||
 | 
					original_name="project_name"
 | 
				
			||||||
 | 
					original_urlname="project_urlname"
 | 
				
			||||||
 | 
					original_description="project_description" 
 | 
				
			||||||
 | 
					TEMPLATE_DIR="./.gitea/templates/flask"
 | 
				
			||||||
 | 
					for filename in $(find ${TEMPLATE_DIR} -name "*.*" -not \( -name "*.git*" -prune \) -not \( -name "apply.sh" -prune \)) 
 | 
				
			||||||
 | 
					do
 | 
				
			||||||
 | 
					    sed -i "s/$original_author/$author/g" $filename
 | 
				
			||||||
 | 
					    sed -i "s/$original_name/$name/g" $filename
 | 
				
			||||||
 | 
					    sed -i "s/$original_urlname/$urlname/g" $filename
 | 
				
			||||||
 | 
					    sed -i "s/$original_description/$description/g" $filename
 | 
				
			||||||
 | 
					    echo "Renamed $filename"
 | 
				
			||||||
 | 
					done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Add requirements
 | 
				
			||||||
 | 
					if [ ! -f pyproject.toml ]
 | 
				
			||||||
 | 
					then
 | 
				
			||||||
 | 
					    cat ${TEMPLATE_DIR}/requirements.txt >> requirements.txt
 | 
				
			||||||
 | 
					    cat ${TEMPLATE_DIR}/requirements-test.txt >> requirements-test.txt
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					    for item in $(cat ${TEMPLATE_DIR}/requirements.txt)
 | 
				
			||||||
 | 
					    do
 | 
				
			||||||
 | 
					        poetry add "${item}"
 | 
				
			||||||
 | 
					    done
 | 
				
			||||||
 | 
					    for item in $(cat ${TEMPLATE_DIR}/requirements-test.txt)
 | 
				
			||||||
 | 
					    do
 | 
				
			||||||
 | 
					        poetry add --dev "${item}"
 | 
				
			||||||
 | 
					    done
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Move module files
 | 
				
			||||||
 | 
					rm -rf "${name}"
 | 
				
			||||||
 | 
					rm -rf tests
 | 
				
			||||||
 | 
					cp -R ${TEMPLATE_DIR}/project_name "${name}"
 | 
				
			||||||
 | 
					cp -R ${TEMPLATE_DIR}/tests tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					cp ${TEMPLATE_DIR}/README.md README.md
 | 
				
			||||||
 | 
					cp ${TEMPLATE_DIR}/Containerfile Containerfile
 | 
				
			||||||
 | 
					cp ${TEMPLATE_DIR}/wsgi.py wsgi.py
 | 
				
			||||||
 | 
					cp ${TEMPLATE_DIR}/.env .env
 | 
				
			||||||
 | 
					cp ${TEMPLATE_DIR}/settings.toml settings.toml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# install
 | 
				
			||||||
 | 
					make clean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if [ ! -f pyproject.toml ]
 | 
				
			||||||
 | 
					then
 | 
				
			||||||
 | 
					    make virtualenv
 | 
				
			||||||
 | 
					    make install
 | 
				
			||||||
 | 
					    echo "Applied Flask template"
 | 
				
			||||||
 | 
					    echo "Ensure you activate your env with 'source .venv/bin/activate'"
 | 
				
			||||||
 | 
					    echo "then run 'project_name' or 'python -m project_name'"
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					    poetry install
 | 
				
			||||||
 | 
					    echo "Applied Flask template"
 | 
				
			||||||
 | 
					    echo "Ensure you activate your env with 'poetry shell'"
 | 
				
			||||||
 | 
					    echo "then run 'project_name' or 'python -m project_name' or 'poetry run project_name'"
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo "README.md has instructions on how to use this Flask application."
 | 
				
			||||||
							
								
								
									
										17
									
								
								docs/index.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								docs/index.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					# Welcome to MkDocs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For full documentation visit [mkdocs.org](https://www.mkdocs.org).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Commands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* `mkdocs new [dir-name]` - Create a new project.
 | 
				
			||||||
 | 
					* `mkdocs serve` - Start the live-reloading docs server.
 | 
				
			||||||
 | 
					* `mkdocs build` - Build the documentation site.
 | 
				
			||||||
 | 
					* `mkdocs -h` - Print help message and exit.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Project layout
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mkdocs.yml    # The configuration file.
 | 
				
			||||||
 | 
					    docs/
 | 
				
			||||||
 | 
					        index.md  # The documentation homepage.
 | 
				
			||||||
 | 
					        ...       # Other markdown pages, images and other files.
 | 
				
			||||||
							
								
								
									
										2
									
								
								mkdocs.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								mkdocs.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
				
			|||||||
 | 
					site_name: project_name
 | 
				
			||||||
 | 
					theme: readthedocs
 | 
				
			||||||
							
								
								
									
										1
									
								
								project_name/VERSION
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								project_name/VERSION
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					0.1.0
 | 
				
			||||||
							
								
								
									
										3
									
								
								project_name/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								project_name/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					from .base import create_app, create_app_wsgi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					__all__ = ["create_app", "create_app_wsgi"]
 | 
				
			||||||
							
								
								
									
										13
									
								
								project_name/__main__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								project_name/__main__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					import click
 | 
				
			||||||
 | 
					from flask.cli import FlaskGroup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from . import create_app_wsgi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@click.group(cls=FlaskGroup, create_app=create_app_wsgi)
 | 
				
			||||||
 | 
					def main():
 | 
				
			||||||
 | 
					    """Management script for the project_name application."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":  # pragma: no cover
 | 
				
			||||||
 | 
					    main()
 | 
				
			||||||
							
								
								
									
										21
									
								
								project_name/base.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								project_name/base.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					from dynaconf import FlaskDynaconf
 | 
				
			||||||
 | 
					from flask import Flask
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def create_app(**config):
 | 
				
			||||||
 | 
					    app = Flask(__name__)
 | 
				
			||||||
 | 
					    FlaskDynaconf(app)  # config managed by Dynaconf
 | 
				
			||||||
 | 
					    app.config.load_extensions(
 | 
				
			||||||
 | 
					        "EXTENSIONS"
 | 
				
			||||||
 | 
					    )  # Load extensions from settings.toml
 | 
				
			||||||
 | 
					    app.config.update(config)  # Override with passed config
 | 
				
			||||||
 | 
					    return app
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def create_app_wsgi():
 | 
				
			||||||
 | 
					    # workaround for Flask issue
 | 
				
			||||||
 | 
					    # that doesn't allow **config
 | 
				
			||||||
 | 
					    # to be passed to create_app
 | 
				
			||||||
 | 
					    # https://github.com/pallets/flask/issues/4170
 | 
				
			||||||
 | 
					    app = create_app()
 | 
				
			||||||
 | 
					    return app
 | 
				
			||||||
							
								
								
									
										0
									
								
								project_name/ext/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								project_name/ext/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										33
									
								
								project_name/ext/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								project_name/ext/admin.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					from flask_admin import Admin
 | 
				
			||||||
 | 
					from flask_admin.base import AdminIndexView
 | 
				
			||||||
 | 
					from flask_admin.contrib import sqla
 | 
				
			||||||
 | 
					from flask_simplelogin import login_required
 | 
				
			||||||
 | 
					from werkzeug.security import generate_password_hash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from project_name.ext.database import db
 | 
				
			||||||
 | 
					from project_name.models import Product, User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Proteck admin with login / Monkey Patch
 | 
				
			||||||
 | 
					AdminIndexView._handle_view = login_required(AdminIndexView._handle_view)
 | 
				
			||||||
 | 
					sqla.ModelView._handle_view = login_required(sqla.ModelView._handle_view)
 | 
				
			||||||
 | 
					admin = Admin()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UserAdmin(sqla.ModelView):
 | 
				
			||||||
 | 
					    column_list = ["username"]
 | 
				
			||||||
 | 
					    can_edit = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def on_model_change(self, form, model, is_created):
 | 
				
			||||||
 | 
					        model.password = generate_password_hash(model.password)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def init_app(app):
 | 
				
			||||||
 | 
					    admin.name = app.config.TITLE
 | 
				
			||||||
 | 
					    admin.template_mode = app.config.FLASK_ADMIN_TEMPLATE_MODE
 | 
				
			||||||
 | 
					    admin.init_app(app)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Add admin page for Product
 | 
				
			||||||
 | 
					    admin.add_view(sqla.ModelView(Product, db.session))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Add admin page for User
 | 
				
			||||||
 | 
					    admin.add_view(UserAdmin(User, db.session))
 | 
				
			||||||
							
								
								
									
										33
									
								
								project_name/ext/auth.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								project_name/ext/auth.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					from flask_simplelogin import SimpleLogin
 | 
				
			||||||
 | 
					from werkzeug.security import check_password_hash, generate_password_hash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from project_name.ext.database import db
 | 
				
			||||||
 | 
					from project_name.models import User
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def verify_login(user):
 | 
				
			||||||
 | 
					    """Validates user login"""
 | 
				
			||||||
 | 
					    username = user.get("username")
 | 
				
			||||||
 | 
					    password = user.get("password")
 | 
				
			||||||
 | 
					    if not username or not password:
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					    existing_user = User.query.filter_by(username=username).first()
 | 
				
			||||||
 | 
					    if not existing_user:
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					    if check_password_hash(existing_user.password, password):
 | 
				
			||||||
 | 
					        return True
 | 
				
			||||||
 | 
					    return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def create_user(username, password):
 | 
				
			||||||
 | 
					    """Creates a new user"""
 | 
				
			||||||
 | 
					    if User.query.filter_by(username=username).first():
 | 
				
			||||||
 | 
					        raise RuntimeError(f"{username} already exists")
 | 
				
			||||||
 | 
					    user = User(username=username, password=generate_password_hash(password))
 | 
				
			||||||
 | 
					    db.session.add(user)
 | 
				
			||||||
 | 
					    db.session.commit()
 | 
				
			||||||
 | 
					    return user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def init_app(app):
 | 
				
			||||||
 | 
					    SimpleLogin(app, login_checker=verify_login)
 | 
				
			||||||
							
								
								
									
										43
									
								
								project_name/ext/commands.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								project_name/ext/commands.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					import click
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from project_name.ext.auth import create_user
 | 
				
			||||||
 | 
					from project_name.ext.database import db
 | 
				
			||||||
 | 
					from project_name.models import Product
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def create_db():
 | 
				
			||||||
 | 
					    """Creates database"""
 | 
				
			||||||
 | 
					    db.create_all()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def drop_db():
 | 
				
			||||||
 | 
					    """Cleans database"""
 | 
				
			||||||
 | 
					    db.drop_all()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def populate_db():
 | 
				
			||||||
 | 
					    """Populate db with sample data"""
 | 
				
			||||||
 | 
					    data = [
 | 
				
			||||||
 | 
					        Product(
 | 
				
			||||||
 | 
					            id=1, name="Ciabatta", price="10", description="Italian Bread"
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        Product(id=2, name="Baguete", price="15", description="French Bread"),
 | 
				
			||||||
 | 
					        Product(id=3, name="Pretzel", price="20", description="German Bread"),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					    db.session.bulk_save_objects(data)
 | 
				
			||||||
 | 
					    db.session.commit()
 | 
				
			||||||
 | 
					    return Product.query.all()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def init_app(app):
 | 
				
			||||||
 | 
					    # add multiple commands in a bulk
 | 
				
			||||||
 | 
					    for command in [create_db, drop_db, populate_db]:
 | 
				
			||||||
 | 
					        app.cli.add_command(app.cli.command()(command))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # add a single command
 | 
				
			||||||
 | 
					    @app.cli.command()
 | 
				
			||||||
 | 
					    @click.option("--username", "-u")
 | 
				
			||||||
 | 
					    @click.option("--password", "-p")
 | 
				
			||||||
 | 
					    def add_user(username, password):
 | 
				
			||||||
 | 
					        """Adds a new user to the database"""
 | 
				
			||||||
 | 
					        return create_user(username, password)
 | 
				
			||||||
							
								
								
									
										7
									
								
								project_name/ext/database.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								project_name/ext/database.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					from flask_sqlalchemy import SQLAlchemy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					db = SQLAlchemy()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def init_app(app):
 | 
				
			||||||
 | 
					    db.init_app(app)
 | 
				
			||||||
							
								
								
									
										13
									
								
								project_name/ext/restapi/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								project_name/ext/restapi/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					from flask import Blueprint
 | 
				
			||||||
 | 
					from flask_restful import Api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .resources import ProductItemResource, ProductResource
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bp = Blueprint("restapi", __name__, url_prefix="/api/v1")
 | 
				
			||||||
 | 
					api = Api(bp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def init_app(app):
 | 
				
			||||||
 | 
					    api.add_resource(ProductResource, "/product/")
 | 
				
			||||||
 | 
					    api.add_resource(ProductItemResource, "/product/<product_id>")
 | 
				
			||||||
 | 
					    app.register_blueprint(bp)
 | 
				
			||||||
							
								
								
									
										35
									
								
								project_name/ext/restapi/resources.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								project_name/ext/restapi/resources.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					from flask import abort, jsonify
 | 
				
			||||||
 | 
					from flask_restful import Resource
 | 
				
			||||||
 | 
					from flask_simplelogin import login_required
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from project_name.models import Product
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ProductResource(Resource):
 | 
				
			||||||
 | 
					    def get(self):
 | 
				
			||||||
 | 
					        products = Product.query.all() or abort(204)
 | 
				
			||||||
 | 
					        return jsonify(
 | 
				
			||||||
 | 
					            {"products": [product.to_dict() for product in products]}
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @login_required(basic=True, username="admin")
 | 
				
			||||||
 | 
					    def post(self):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Creates a new product.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Only admin user authenticated using basic auth can post
 | 
				
			||||||
 | 
					        Basic takes base64 encripted username:password.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # curl -XPOST localhost:5000/api/v1/product/ \
 | 
				
			||||||
 | 
					        #  -H "Authorization: Basic Y2h1Y2s6bm9ycmlz" \
 | 
				
			||||||
 | 
					        #  -H "Content-Type: application/json"
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return NotImplementedError(
 | 
				
			||||||
 | 
					            "Someone please complete this example and send a PR :)"
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ProductItemResource(Resource):
 | 
				
			||||||
 | 
					    def get(self, product_id):
 | 
				
			||||||
 | 
					        product = Product.query.filter_by(id=product_id).first() or abort(404)
 | 
				
			||||||
 | 
					        return jsonify(product.to_dict())
 | 
				
			||||||
							
								
								
									
										16
									
								
								project_name/ext/webui/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								project_name/ext/webui/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					from flask import Blueprint
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .views import index, only_admin, product, secret
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bp = Blueprint("webui", __name__, template_folder="templates")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bp.add_url_rule("/", view_func=index)
 | 
				
			||||||
 | 
					bp.add_url_rule(
 | 
				
			||||||
 | 
					    "/product/<product_id>", view_func=product, endpoint="productview"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					bp.add_url_rule("/secret", view_func=secret, endpoint="secret")
 | 
				
			||||||
 | 
					bp.add_url_rule("/only_admin", view_func=only_admin, endpoint="onlyadmin")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def init_app(app):
 | 
				
			||||||
 | 
					    app.register_blueprint(bp)
 | 
				
			||||||
							
								
								
									
										29
									
								
								project_name/ext/webui/templates/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								project_name/ext/webui/templates/index.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					{% extends "bootstrap/base.html" %}
 | 
				
			||||||
 | 
					{% block title %}{{config.get('TITLE')}}{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block navbar %}
 | 
				
			||||||
 | 
					<div class="navbar">
 | 
				
			||||||
 | 
					<div class="navbar-header">
 | 
				
			||||||
 | 
					    <a class="navbar-brand" href="#">
 | 
				
			||||||
 | 
					    <img alt="Brand" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAB+0lEQVR4AcyYg5LkUBhG+1X2PdZGaW3btm3btm3bHttWrPomd1r/2Jn/VJ02TpxcH4CQ/dsuazWgzbIdrm9dZVd4pBz4zx2igTaFHrhvjneVXNHCSqIlFEjiwMyyyOBilRgGSqLNF1jnwNQdIvAt48C3IlBmHCiLQHC2zoHDu6zG1iXn6+y62ScxY9AODO6w0pvAqf23oSE4joOfH6OxfMoRnoGUm+de8wykbFt6wZtA07QwtNOqKh3ZbS3Wzz2F+1c/QJY0UCJ/J3kXWJfv7VhxCRRV1jGw7XI+gcO7rEFFRvdYxydwcPsVsC0bQdKScngt4iUTD4Fy/8p7PoHzRu1DclwmgmiqgUXjD3oTKHbAt869qdJ7l98jNTEblPTkXMwetpvnftA0LLHb4X8kiY9Kx6Q+W7wJtG0HR7fdrtYz+x7iya0vkEtUULIzCjC21wY+W/GYXusRH5kGytWTLxgEEhePPwhKYb7EK3BQuxWwTBuUkd3X8goUn6fMHLyTT+DCsQdAEXNzSMeVPAJHdF2DmH8poCREp3uwm7HsGq9J9q69iuunX6EgrwQVObjpBt8z6rdPfvE8kiiyhsvHnomrQx6BxYUyYiNS8f75H1w4/ISepDZLoDhNJ9cdNUquhRsv+6EP9oNH7Iff2A9g8h8CLt1gH0Qf9NMQAFnO60BJFQe0AAAAAElFTkSuQmCC">
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					<div class="container">
 | 
				
			||||||
 | 
					<h1>{{config.get('TITLE')}}</h1>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="jumbotron">
 | 
				
			||||||
 | 
					<ul class="list-group">
 | 
				
			||||||
 | 
					    {% for product in products %}
 | 
				
			||||||
 | 
					    <li class="list-group-item">
 | 
				
			||||||
 | 
					        <a href="{{url_for('webui.productview', product_id=product.id)}}">{{product.name}}- {{ "%0.2f" | format(product.price)}}</a>
 | 
				
			||||||
 | 
					    </li>
 | 
				
			||||||
 | 
					    {% endfor %}
 | 
				
			||||||
 | 
					</ul>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										13
									
								
								project_name/ext/webui/templates/product.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								project_name/ext/webui/templates/product.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					{% extends "index.html" %}
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					<div class="container">
 | 
				
			||||||
 | 
					<h1>{{ product.name }}</h1>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<div class="jumbotron">
 | 
				
			||||||
 | 
					<h2>R$ {{ "%0.2f" | format(product.price)}}</h2>
 | 
				
			||||||
 | 
					<p>
 | 
				
			||||||
 | 
					    {{product.description}}
 | 
				
			||||||
 | 
					</p>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										26
									
								
								project_name/ext/webui/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								project_name/ext/webui/views.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					from flask import abort, render_template
 | 
				
			||||||
 | 
					from flask_simplelogin import login_required
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from project_name.models import Product
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def index():
 | 
				
			||||||
 | 
					    products = Product.query.all()
 | 
				
			||||||
 | 
					    return render_template("index.html", products=products)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def product(product_id):
 | 
				
			||||||
 | 
					    product = Product.query.filter_by(id=product_id).first() or abort(
 | 
				
			||||||
 | 
					        404, "produto nao encontrado"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    return render_template("product.html", product=product)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@login_required
 | 
				
			||||||
 | 
					def secret():
 | 
				
			||||||
 | 
					    return "This can be seen only if user is logged in"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@login_required(username="admin")
 | 
				
			||||||
 | 
					def only_admin():
 | 
				
			||||||
 | 
					    return "only admin user can see this text"
 | 
				
			||||||
							
								
								
									
										16
									
								
								project_name/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								project_name/models.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					from sqlalchemy_serializer import SerializerMixin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from project_name.ext.database import db
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Product(db.Model, SerializerMixin):
 | 
				
			||||||
 | 
					    id = db.Column(db.Integer, primary_key=True)
 | 
				
			||||||
 | 
					    name = db.Column(db.String(140))
 | 
				
			||||||
 | 
					    price = db.Column(db.Numeric())
 | 
				
			||||||
 | 
					    description = db.Column(db.Text)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class User(db.Model, SerializerMixin):
 | 
				
			||||||
 | 
					    id = db.Column(db.Integer, primary_key=True)
 | 
				
			||||||
 | 
					    username = db.Column(db.String(140))
 | 
				
			||||||
 | 
					    password = db.Column(db.String(512))
 | 
				
			||||||
							
								
								
									
										10
									
								
								requirements-base.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								requirements-base.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					pytest
 | 
				
			||||||
 | 
					coverage
 | 
				
			||||||
 | 
					flake8
 | 
				
			||||||
 | 
					black
 | 
				
			||||||
 | 
					isort
 | 
				
			||||||
 | 
					pytest-cov
 | 
				
			||||||
 | 
					codecov
 | 
				
			||||||
 | 
					mypy
 | 
				
			||||||
 | 
					gitchangelog
 | 
				
			||||||
 | 
					mkdocs
 | 
				
			||||||
							
								
								
									
										5
									
								
								requirements-test.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								requirements-test.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					flask-debugtoolbar
 | 
				
			||||||
 | 
					flask-shell-ipython
 | 
				
			||||||
 | 
					ipdb
 | 
				
			||||||
 | 
					pytest-flask
 | 
				
			||||||
 | 
					python-dotenv
 | 
				
			||||||
							
								
								
									
										9
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					flask
 | 
				
			||||||
 | 
					flask-admin
 | 
				
			||||||
 | 
					flask-simplelogin
 | 
				
			||||||
 | 
					flask-bootstrap
 | 
				
			||||||
 | 
					flask-sqlalchemy
 | 
				
			||||||
 | 
					sqlalchemy-serializer
 | 
				
			||||||
 | 
					dynaconf
 | 
				
			||||||
 | 
					click
 | 
				
			||||||
 | 
					flask-restful
 | 
				
			||||||
							
								
								
									
										47
									
								
								settings.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								settings.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					[default]
 | 
				
			||||||
 | 
					DEBUG = false
 | 
				
			||||||
 | 
					FLASK_ADMIN_TEMPLATE_MODE = "bootstrap3"
 | 
				
			||||||
 | 
					FLASK_ADMIN_SWATCH = 'cerulean'
 | 
				
			||||||
 | 
					SQLALCHEMY_DATABASE_URI = 'sqlite:///development.db'
 | 
				
			||||||
 | 
					TITLE = "project_name"
 | 
				
			||||||
 | 
					SECRET_KEY = "Pl3453Ch4ng3"
 | 
				
			||||||
 | 
					PASSWORD_SCHEMES = ['pbkdf2_sha512', 'md5_crypt']
 | 
				
			||||||
 | 
					EXTENSIONS = [
 | 
				
			||||||
 | 
					    "flask_bootstrap:Bootstrap",
 | 
				
			||||||
 | 
					    "project_name.ext.database:init_app",
 | 
				
			||||||
 | 
					    "project_name.ext.auth:init_app",
 | 
				
			||||||
 | 
					    "project_name.ext.admin:init_app",
 | 
				
			||||||
 | 
					    "project_name.ext.commands:init_app",
 | 
				
			||||||
 | 
					    "project_name.ext.webui:init_app",
 | 
				
			||||||
 | 
					    "project_name.ext.restapi:init_app",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[development]
 | 
				
			||||||
 | 
					EXTENSIONS = [
 | 
				
			||||||
 | 
					    "flask_debugtoolbar:DebugToolbarExtension",
 | 
				
			||||||
 | 
					    "dynaconf_merge_unique"  # to reuse extensions list from [default]
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					TEMPLATES_AUTO_RELOAD = true
 | 
				
			||||||
 | 
					DEBUG = true
 | 
				
			||||||
 | 
					DEBUG_TOOLBAR_ENABLED = true
 | 
				
			||||||
 | 
					DEBUG_TB_INTERCEPT_REDIRECTS = false
 | 
				
			||||||
 | 
					DEBUG_TB_PROFILER_ENABLED = true
 | 
				
			||||||
 | 
					DEBUG_TB_TEMPLATE_EDITOR_ENABLED = true
 | 
				
			||||||
 | 
					DEBUG_TB_PANELS = [
 | 
				
			||||||
 | 
					    "flask_debugtoolbar.panels.versions.VersionDebugPanel",
 | 
				
			||||||
 | 
					    "flask_debugtoolbar.panels.sqlalchemy.SQLAlchemyDebugPanel",
 | 
				
			||||||
 | 
					    "flask_debugtoolbar.panels.timer.TimerDebugPanel",
 | 
				
			||||||
 | 
					    "flask_debugtoolbar.panels.headers.HeaderDebugPanel",
 | 
				
			||||||
 | 
					    "flask_debugtoolbar.panels.request_vars.RequestVarsDebugPanel",
 | 
				
			||||||
 | 
					    "flask_debugtoolbar.panels.template.TemplateDebugPanel",
 | 
				
			||||||
 | 
					    "flask_debugtoolbar.panels.route_list.RouteListDebugPanel",
 | 
				
			||||||
 | 
					    "flask_debugtoolbar.panels.logger.LoggingPanel",
 | 
				
			||||||
 | 
					    "flask_debugtoolbar.panels.profiler.ProfilerDebugPanel",
 | 
				
			||||||
 | 
					    "flask_debugtoolbar.panels.config_vars.ConfigVarsDebugPanel"
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[testing]
 | 
				
			||||||
 | 
					SQLALCHEMY_DATABASE_URI = 'sqlite:///testing.db'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[production]
 | 
				
			||||||
 | 
					SQLALCHEMY_DATABASE_URI = 'sqlite:///production.db'
 | 
				
			||||||
							
								
								
									
										49
									
								
								setup.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								setup.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					"""Python setup.py for project_name package"""
 | 
				
			||||||
 | 
					import io
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					from setuptools import find_packages, setup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def read(*paths, **kwargs):
 | 
				
			||||||
 | 
					    """Read the contents of a text file safely.
 | 
				
			||||||
 | 
					    >>> read("project_name", "VERSION")
 | 
				
			||||||
 | 
					    '0.1.0'
 | 
				
			||||||
 | 
					    >>> read("README.md")
 | 
				
			||||||
 | 
					    ...
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    content = ""
 | 
				
			||||||
 | 
					    with io.open(
 | 
				
			||||||
 | 
					        os.path.join(os.path.dirname(__file__), *paths),
 | 
				
			||||||
 | 
					        encoding=kwargs.get("encoding", "utf8"),
 | 
				
			||||||
 | 
					    ) as open_file:
 | 
				
			||||||
 | 
					        content = open_file.read().strip()
 | 
				
			||||||
 | 
					    return content
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def read_requirements(path):
 | 
				
			||||||
 | 
					    return [
 | 
				
			||||||
 | 
					        line.strip()
 | 
				
			||||||
 | 
					        for line in read(path).split("\n")
 | 
				
			||||||
 | 
					        if not line.startswith(('"', "#", "-", "git+"))
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					setup(
 | 
				
			||||||
 | 
					    name="project_name",
 | 
				
			||||||
 | 
					    version=read("project_name", "VERSION"),
 | 
				
			||||||
 | 
					    description="project_description",
 | 
				
			||||||
 | 
					    url="https://git.disi.dev/author_name/project_urlname/",
 | 
				
			||||||
 | 
					    long_description=read("README.md"),
 | 
				
			||||||
 | 
					    long_description_content_type="text/markdown",
 | 
				
			||||||
 | 
					    author="author_name",
 | 
				
			||||||
 | 
					    packages=find_packages(exclude=["tests", ".gitea"]),
 | 
				
			||||||
 | 
					    install_requires=read_requirements("requirements.txt"),
 | 
				
			||||||
 | 
					    entry_points={
 | 
				
			||||||
 | 
					        "console_scripts": ["project_name = project_name.__main__:main"]
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    extras_require={
 | 
				
			||||||
 | 
					        "test": read_requirements("requirements-test.txt")
 | 
				
			||||||
 | 
					        + read_requirements("requirements-base.txt")
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										0
									
								
								tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										33
									
								
								tests/conftest.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								tests/conftest.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					import sys
 | 
				
			||||||
 | 
					import pytest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from project_name import create_app
 | 
				
			||||||
 | 
					from project_name.ext.commands import populate_db
 | 
				
			||||||
 | 
					from project_name.ext.database import db
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pytest.fixture(scope="session")
 | 
				
			||||||
 | 
					def app():
 | 
				
			||||||
 | 
					    app = create_app(FORCE_ENV_FOR_DYNACONF="testing")
 | 
				
			||||||
 | 
					    with app.app_context():
 | 
				
			||||||
 | 
					        db.create_all(app=app)
 | 
				
			||||||
 | 
					        yield app
 | 
				
			||||||
 | 
					        db.drop_all(app=app)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pytest.fixture(scope="session")
 | 
				
			||||||
 | 
					def products(app):
 | 
				
			||||||
 | 
					    with app.app_context():
 | 
				
			||||||
 | 
					        return populate_db()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# each test runs on cwd to its temp dir
 | 
				
			||||||
 | 
					@pytest.fixture(autouse=True)
 | 
				
			||||||
 | 
					def go_to_tmpdir(request):
 | 
				
			||||||
 | 
					    # Get the fixture dynamically by its name.
 | 
				
			||||||
 | 
					    tmpdir = request.getfixturevalue("tmpdir")
 | 
				
			||||||
 | 
					    # ensure local test created packages can be imported
 | 
				
			||||||
 | 
					    sys.path.insert(0, str(tmpdir))
 | 
				
			||||||
 | 
					    # Chdir only for the duration of the test.
 | 
				
			||||||
 | 
					    with tmpdir.as_cwd():
 | 
				
			||||||
 | 
					        yield
 | 
				
			||||||
							
								
								
									
										28
									
								
								tests/test_api.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								tests/test_api.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					from decimal import Decimal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_products_get_all(client, products):  # Arrange
 | 
				
			||||||
 | 
					    """Test get all products"""
 | 
				
			||||||
 | 
					    # Act
 | 
				
			||||||
 | 
					    response = client.get("/api/v1/product/")
 | 
				
			||||||
 | 
					    # Assert
 | 
				
			||||||
 | 
					    assert response.status_code == 200
 | 
				
			||||||
 | 
					    data = response.json["products"]
 | 
				
			||||||
 | 
					    assert len(data) == 3
 | 
				
			||||||
 | 
					    for product in products:
 | 
				
			||||||
 | 
					        assert product.id in [item["id"] for item in data]
 | 
				
			||||||
 | 
					        assert product.name in [item["name"] for item in data]
 | 
				
			||||||
 | 
					        assert product.price in [Decimal(item["price"]) for item in data]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_products_get_one(client, products):  # Arrange
 | 
				
			||||||
 | 
					    """Test get one product"""
 | 
				
			||||||
 | 
					    for product in products:
 | 
				
			||||||
 | 
					        # Act
 | 
				
			||||||
 | 
					        response = client.get(f"/api/v1/product/{product.id}")
 | 
				
			||||||
 | 
					        data = response.json
 | 
				
			||||||
 | 
					        # Assert
 | 
				
			||||||
 | 
					        assert response.status_code == 200
 | 
				
			||||||
 | 
					        assert data["name"] == product.name
 | 
				
			||||||
 | 
					        assert Decimal(data["price"]) == product.price
 | 
				
			||||||
 | 
					        assert data["description"] == product.description
 | 
				
			||||||
		Reference in New Issue
	
	Block a user