forked from eric.li/EditionPN
first commit
This commit is contained in:
commit
361bae10ac
11
app/__init__.py
Normal file
11
app/__init__.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
from flask import Flask
|
||||||
|
from config import Config
|
||||||
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
from flask_migrate import Migrate
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config.from_object(Config)
|
||||||
|
db = SQLAlchemy(app)
|
||||||
|
migrate = Migrate(app, db)
|
||||||
|
|
||||||
|
from app import routes, models
|
97
app/forms.py
Normal file
97
app/forms.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from flask_wtf.file import FileAllowed
|
||||||
|
from wtforms import StringField, SubmitField, FileField, TextAreaField, RadioField
|
||||||
|
from wtforms.validators import DataRequired, NumberRange, Length, Regexp
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
import os
|
||||||
|
|
||||||
|
REPERTOIRE_YAML = "./export/"
|
||||||
|
|
||||||
|
if not os.path.exists(REPERTOIRE_YAML):
|
||||||
|
os.makedirs(REPERTOIRE_YAML)
|
||||||
|
|
||||||
|
class Form(FlaskForm):
|
||||||
|
|
||||||
|
exporter = SubmitField("Exporter")
|
||||||
|
fichier = FileField("Choisir fichier", validators=[FileAllowed(["yml"], "Fichier Yaml seulement!")])
|
||||||
|
importer = SubmitField("Importer")
|
||||||
|
|
||||||
|
class PNForm(Form):
|
||||||
|
file_length = len("PN0")
|
||||||
|
|
||||||
|
code = StringField("Code", validators=[DataRequired(), Length(3,3)])
|
||||||
|
nom = StringField("Nom", validators=[DataRequired()] )
|
||||||
|
diminutif = StringField("Diminutif", validators=[DataRequired()] )
|
||||||
|
description = TextAreaField("Description", validators=[DataRequired()] )
|
||||||
|
type = RadioField("Type", choices=[1,2,3], validators=[DataRequired()])
|
||||||
|
|
||||||
|
class ACForm(Form):
|
||||||
|
file_length = len("AC0000.yml")
|
||||||
|
|
||||||
|
code = StringField("Code", validators=[DataRequired(), Length(6,6)])
|
||||||
|
|
||||||
|
|
||||||
|
class SAEForm(Form):
|
||||||
|
file_length = len("SAE00.yml")
|
||||||
|
|
||||||
|
code = StringField("Code", validators=[DataRequired(), Length(5,5)])
|
||||||
|
titre = StringField("Titre", validators=[DataRequired()] )
|
||||||
|
semestre = StringField("Semestre", validators=[DataRequired()] )
|
||||||
|
heures_encadrees = StringField("Heures encadrées", validators=[DataRequired()] )
|
||||||
|
heures_tp = StringField("Heures TP", validators=[DataRequired()] )
|
||||||
|
projet = StringField("Projet", validators=[DataRequired()] )
|
||||||
|
description = TextAreaField("Description", validators=[DataRequired()] )
|
||||||
|
coef = StringField("Coef.", validators=[DataRequired()] )
|
||||||
|
acs = StringField("ACs", validators=[DataRequired()] )
|
||||||
|
ressources = StringField("Ressources", validators=[DataRequired()] )
|
||||||
|
livrables = TextAreaField("Livrables", validators=[DataRequired()] )
|
||||||
|
motscles = StringField("Mots clés", validators=[DataRequired()] )
|
||||||
|
|
||||||
|
|
||||||
|
class RessourceForm(Form):
|
||||||
|
file_length = len("R000.yml")
|
||||||
|
|
||||||
|
code = StringField("Code", validators=[DataRequired(), Length(4,4)])
|
||||||
|
nom = StringField("Nom", validators=[DataRequired()] )
|
||||||
|
semestre = StringField("Semestre", validators=[DataRequired()] )
|
||||||
|
heures_formation = StringField("Heures formation", validators=[DataRequired()] )
|
||||||
|
heures_tp = StringField("Heures TP", validators=[DataRequired()] )
|
||||||
|
coef = StringField("Coef.", validators=[DataRequired()] )
|
||||||
|
acs = StringField("ACs", validators=[DataRequired()] )
|
||||||
|
saes = StringField("SAEs", validators=[DataRequired()] )
|
||||||
|
prerequis = StringField("Prérequis", validators=[DataRequired()] )
|
||||||
|
contexte = TextAreaField("Contexte", validators=[DataRequired()] )
|
||||||
|
contenu = TextAreaField("Contenu", validators=[DataRequired()] )
|
||||||
|
motscles = StringField("Mots clés", validators=[DataRequired()] )
|
||||||
|
|
||||||
|
class CompetenceForm(Form):
|
||||||
|
file_length = len("RT0.yml")
|
||||||
|
|
||||||
|
code = StringField("Code", validators=[DataRequired(), Length(3,3)])
|
||||||
|
nom = StringField("Nom", validators=[DataRequired()] )
|
||||||
|
diminutif = StringField("Diminutif", validators=[DataRequired()] )
|
||||||
|
description = TextAreaField("Description", validators=[DataRequired()] )
|
||||||
|
composantes = TextAreaField("Composantes", validators=[DataRequired()] )
|
||||||
|
situations = TextAreaField("Situations", validators=[DataRequired()] )
|
||||||
|
niveaux = TextAreaField("Niveaux", validators=[DataRequired()] )
|
||||||
|
|
||||||
|
def form_import(form):
|
||||||
|
""" Si import a été appuyé et qu'il n'y a pas d'erreur d'import => importe le fichier yaml"""
|
||||||
|
if form.importer.data and len(form.fichier.errors) == 0 and len(form.fichier.data.filename) == form.file_length:
|
||||||
|
fichier_Yaml = yaml.safe_load(form.fichier.data.read())
|
||||||
|
for categorie, valeur in fichier_Yaml.items():
|
||||||
|
form[categorie].data = valeur
|
||||||
|
form.validate_on_submit() # Réinitialise les messages d'erreur
|
||||||
|
return form
|
||||||
|
|
||||||
|
def form_export(form):
|
||||||
|
""" Si le formulaire est valide => exporte dans un fichier yaml avec les informations du formulaire """
|
||||||
|
output = {}
|
||||||
|
|
||||||
|
for categorie, valeur in list(form.data.items())[3:-1]:
|
||||||
|
output[categorie] = valeur
|
||||||
|
|
||||||
|
fichier = REPERTOIRE_YAML + form.code.data + ".yml"
|
||||||
|
with open(fichier, "w", encoding="utf8") as fid:
|
||||||
|
fid.write(yaml.dump(output))
|
11
app/models.py
Normal file
11
app/models.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
from app import db
|
||||||
|
|
||||||
|
class PN(db.Model):
|
||||||
|
code = db.Column(db.String(3), primary_key = True)
|
||||||
|
nom = db.Column(db.String(255))
|
||||||
|
diminutif = db.Column(db.String(30))
|
||||||
|
description = db.Column(db.Text())
|
||||||
|
type = db.Column(db.Integer())
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<PN {}>".format(self.code)
|
59
app/routes.py
Normal file
59
app/routes.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
from flask import render_template, flash, redirect, url_for, request
|
||||||
|
from app import app
|
||||||
|
from app.forms import *
|
||||||
|
from app.models import PN
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
@app.route("/index")
|
||||||
|
def index():
|
||||||
|
return render_template("base.html")
|
||||||
|
|
||||||
|
@app.route("/PN", methods=["GET","POST"])
|
||||||
|
def PN():
|
||||||
|
form = PNForm()
|
||||||
|
form_validation = form.validate_on_submit()
|
||||||
|
form = form_import(form)
|
||||||
|
if form_validation and form.exporter.data:
|
||||||
|
flash("Ajout du référentiel PN: {} ".format(form.code.data))
|
||||||
|
form_export(form)
|
||||||
|
return redirect(url_for("PN"))
|
||||||
|
return render_template("PN.html", form = form)
|
||||||
|
|
||||||
|
@app.route("/AC", methods=["GET","POST"])
|
||||||
|
def AC():
|
||||||
|
return render_template("PN.html", form = form)
|
||||||
|
|
||||||
|
@app.route("/SAE", methods=["GET","POST"])
|
||||||
|
def SAE():
|
||||||
|
form = SAEForm()
|
||||||
|
form_validation = form.validate_on_submit()
|
||||||
|
form = form_import(form)
|
||||||
|
if form_validation and form.exporter.data:
|
||||||
|
flash("Ajout du référentiel SAE: {} ".format(form.code.data))
|
||||||
|
form_export(form)
|
||||||
|
return redirect(url_for("SAE"))
|
||||||
|
return render_template("SAE.html", form = form)
|
||||||
|
|
||||||
|
@app.route("/Ressource", methods=["GET","POST"])
|
||||||
|
def Ressource():
|
||||||
|
form = RessourceForm()
|
||||||
|
form_validation = form.validate_on_submit()
|
||||||
|
form = form_import(form)
|
||||||
|
if form_validation and form.exporter.data:
|
||||||
|
flash("Ajout du référentiel Ressource: {} ".format(form.code.data))
|
||||||
|
form_export(form)
|
||||||
|
return redirect(url_for("Ressource"))
|
||||||
|
return render_template("Ressource.html", form = form)
|
||||||
|
|
||||||
|
@app.route("/Competence", methods=["GET","POST"])
|
||||||
|
def Competence():
|
||||||
|
form = CompetenceForm()
|
||||||
|
form_validation = form.validate_on_submit()
|
||||||
|
form = form_import(form)
|
||||||
|
if form_validation and form.exporter.data:
|
||||||
|
flash("Ajout du référentielCompetence: {} ".format(form.code.data))
|
||||||
|
form_export(form)
|
||||||
|
return redirect(url_for("Competence"))
|
||||||
|
return render_template("Competence.html", form = form)
|
16
app/templates/Competence.html
Normal file
16
app/templates/Competence.html
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{% extends "form.html" %}
|
||||||
|
|
||||||
|
{% block title %}RT Form{% endblock %}
|
||||||
|
{% block form_title %}Formulaire de Compétences (RT){% endblock %}
|
||||||
|
|
||||||
|
{% block formulaire %}
|
||||||
|
|
||||||
|
{{ render_field(form.code,"input") }}
|
||||||
|
{{ render_field(form.nom,"input") }}
|
||||||
|
{{ render_field(form.diminutif,"input") }}
|
||||||
|
{{ render_field(form.description,"textarea") }}
|
||||||
|
{{ render_field(form.composantes,"textarea") }}
|
||||||
|
{{ render_field(form.situations,"textarea") }}
|
||||||
|
{{ render_field(form.niveaux,"textarea") }}
|
||||||
|
|
||||||
|
{% endblock %}
|
28
app/templates/PN.html
Normal file
28
app/templates/PN.html
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{% extends "form.html" %}
|
||||||
|
|
||||||
|
{% block title %}PN Form{% endblock %}
|
||||||
|
{% block form_title %}Formulaire de Programmes Nationaux (PN){% endblock %}
|
||||||
|
|
||||||
|
{% block formulaire %}
|
||||||
|
|
||||||
|
{{ render_field(form.code,"input") }}
|
||||||
|
{{ render_field(form.nom,"input") }}
|
||||||
|
{{ render_field(form.diminutif,"input") }}
|
||||||
|
{{ render_field(form.description,"textarea") }}
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">{{ form.type.label }}</label>
|
||||||
|
<div class="control">
|
||||||
|
{% for num in form.type %}
|
||||||
|
<label class="radio">
|
||||||
|
{{ num(class="radio") }}
|
||||||
|
{{ num.label }}
|
||||||
|
</label>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% for error in form.type.errors %}
|
||||||
|
<p class="help is-danger">{{error}}</p>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
21
app/templates/Ressource.html
Normal file
21
app/templates/Ressource.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{% extends "form.html" %}
|
||||||
|
|
||||||
|
{% block title %}Ressource Form{% endblock %}
|
||||||
|
{% block form_title %}Formulaire de Ressources (R){% endblock %}
|
||||||
|
|
||||||
|
{% block formulaire %}
|
||||||
|
|
||||||
|
{{ render_field(form.code,"input") }}
|
||||||
|
{{ render_field(form.nom,"input") }}
|
||||||
|
{{ render_field(form.semestre,"input") }}
|
||||||
|
{{ render_field(form.heures_formation,"input") }}
|
||||||
|
{{ render_field(form.heures_tp,"input") }}
|
||||||
|
{{ render_field(form.coef,"input") }}
|
||||||
|
{{ render_field(form.acs,"input") }}
|
||||||
|
{{ render_field(form.saes,"input") }}
|
||||||
|
{{ render_field(form.prerequis,"input") }}
|
||||||
|
{{ render_field(form.contexte,"textarea") }}
|
||||||
|
{{ render_field(form.contenu,"textarea") }}
|
||||||
|
{{ render_field(form.motscles,"input") }}
|
||||||
|
|
||||||
|
{% endblock %}
|
21
app/templates/SAE.html
Normal file
21
app/templates/SAE.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{% extends "form.html" %}
|
||||||
|
|
||||||
|
{% block title %}SAE Form{% endblock %}
|
||||||
|
{% block form_title %}Formulaire de Situations d'apprentissages et d'évaluations (SAE){% endblock %}
|
||||||
|
|
||||||
|
{% block formulaire %}
|
||||||
|
|
||||||
|
{{ render_field(form.code,"input") }}
|
||||||
|
{{ render_field(form.titre,"input") }}
|
||||||
|
{{ render_field(form.semestre,"input") }}
|
||||||
|
{{ render_field(form.heures_encadrees,"input") }}
|
||||||
|
{{ render_field(form.heures_tp,"input") }}
|
||||||
|
{{ render_field(form.projet,"input") }}
|
||||||
|
{{ render_field(form.description,"textarea") }}
|
||||||
|
{{ render_field(form.coef,"input") }}
|
||||||
|
{{ render_field(form.acs,"input") }}
|
||||||
|
{{ render_field(form.ressources,"input") }}
|
||||||
|
{{ render_field(form.livrables,"textarea") }}
|
||||||
|
{{ render_field(form.motscles,"input") }}
|
||||||
|
|
||||||
|
{% endblock %}
|
54
app/templates/base.html
Normal file
54
app/templates/base.html
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="has-navbar-fixed-top">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>{% block title %}{% endblock %}</title>
|
||||||
|
<link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.2/css/bulma.min.css">
|
||||||
|
<link type="text/css" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||||
|
{% block head %}{% endblock %}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Barre de navigation -->
|
||||||
|
<nav class="navbar is-primary is-spaced is-fixed-top py-0">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-brand">
|
||||||
|
<a class="navbar-item is-size-5" href="https://scodoc.org/">ScoDoc</a>
|
||||||
|
<!-- Menu deroulant affiché uniquement sur mobile -->
|
||||||
|
<div class="navbar-burger">
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-menu">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<!-- Liste des catégories -->
|
||||||
|
<a class="navbar-item" href="{{ url_for('PN') }}">PN</a>
|
||||||
|
<a class="navbar-item" href="{{ url_for('AC') }}">AC</a>
|
||||||
|
<a class="navbar-item" href="{{ url_for('SAE') }}">SAÉ</a>
|
||||||
|
<a class="navbar-item" href="{{ url_for('Ressource') }}">Ressource</a>
|
||||||
|
<a class="navbar-item" href="{{ url_for('Competence') }}">Competence</a>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<!-- Contenu de la page -->
|
||||||
|
<div class="container">
|
||||||
|
<div class="box">
|
||||||
|
{% with messages = get_flashed_messages() %}
|
||||||
|
{% if messages %}
|
||||||
|
<script>
|
||||||
|
alert("{% for message in messages %}{{message}}{% endfor %}");
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
48
app/templates/form.html
Normal file
48
app/templates/form.html
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{% macro render_field(field,class) %}
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">{{ field.label }}</label>
|
||||||
|
<div class="control">
|
||||||
|
{{ field(class=class)}}
|
||||||
|
</div>
|
||||||
|
{% for error in field.errors %}
|
||||||
|
<p class="help is-danger">{{error}}</p>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<form action="" enctype=multipart/form-data method="post" novalidate>
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
<div class="content">
|
||||||
|
<h1 class="title">{% block form_title %}{% endblock %}</h1>
|
||||||
|
|
||||||
|
{% block formulaire %}{% endblock %}
|
||||||
|
|
||||||
|
<div class="field is-grouped">
|
||||||
|
<div class="control">
|
||||||
|
{{ form.exporter(class="button")}}
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<div class="file has-name">
|
||||||
|
<label class="file-label">
|
||||||
|
{{ form.fichier(class="file-input") }}
|
||||||
|
<span class="file-cta">
|
||||||
|
<span class="file-icon">
|
||||||
|
<i class="fas fa-file-import"></i>
|
||||||
|
</span>
|
||||||
|
<span class="file-label">
|
||||||
|
{{ form.fichier.label}}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
{{ form.importer(class="button file-name", accept=".yml")}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
{% for error in form.fichier.errors %}
|
||||||
|
<p class="help is-danger">{{error}}</p>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
7
config.py
Normal file
7
config.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import os
|
||||||
|
basedir = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
class Config(object):
|
||||||
|
SECRET_KEY = os.environ.get("SECRET_KEY") or 'unStringRandomDuneLongueurRandom'
|
||||||
|
SQLALCHEMY_DATABASE_URI = os.environ.get("DATABASE_URL") or "sqlite:///" + os.path.join(basedir, "app.db")
|
||||||
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
7
main.py
Normal file
7
main.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from app import app, db
|
||||||
|
from app.models import PN
|
||||||
|
|
||||||
|
@app.shell_context_processor
|
||||||
|
def make_shell_context():
|
||||||
|
return {"db": db, "PN": PN}
|
||||||
|
|
1
migrations/README
Normal file
1
migrations/README
Normal file
@ -0,0 +1 @@
|
|||||||
|
Generic single-database configuration.
|
50
migrations/alembic.ini
Normal file
50
migrations/alembic.ini
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# A generic, single database configuration.
|
||||||
|
|
||||||
|
[alembic]
|
||||||
|
# template used to generate migration files
|
||||||
|
# file_template = %%(rev)s_%%(slug)s
|
||||||
|
|
||||||
|
# set to 'true' to run the environment during
|
||||||
|
# the 'revision' command, regardless of autogenerate
|
||||||
|
# revision_environment = false
|
||||||
|
|
||||||
|
|
||||||
|
# Logging configuration
|
||||||
|
[loggers]
|
||||||
|
keys = root,sqlalchemy,alembic,flask_migrate
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys = console
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys = generic
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level = WARN
|
||||||
|
handlers = console
|
||||||
|
qualname =
|
||||||
|
|
||||||
|
[logger_sqlalchemy]
|
||||||
|
level = WARN
|
||||||
|
handlers =
|
||||||
|
qualname = sqlalchemy.engine
|
||||||
|
|
||||||
|
[logger_alembic]
|
||||||
|
level = INFO
|
||||||
|
handlers =
|
||||||
|
qualname = alembic
|
||||||
|
|
||||||
|
[logger_flask_migrate]
|
||||||
|
level = INFO
|
||||||
|
handlers =
|
||||||
|
qualname = flask_migrate
|
||||||
|
|
||||||
|
[handler_console]
|
||||||
|
class = StreamHandler
|
||||||
|
args = (sys.stderr,)
|
||||||
|
level = NOTSET
|
||||||
|
formatter = generic
|
||||||
|
|
||||||
|
[formatter_generic]
|
||||||
|
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||||
|
datefmt = %H:%M:%S
|
90
migrations/env.py
Normal file
90
migrations/env.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from logging.config import fileConfig
|
||||||
|
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
|
from alembic import context
|
||||||
|
|
||||||
|
# this is the Alembic Config object, which provides
|
||||||
|
# access to the values within the .ini file in use.
|
||||||
|
config = context.config
|
||||||
|
|
||||||
|
# Interpret the config file for Python logging.
|
||||||
|
# This line sets up loggers basically.
|
||||||
|
fileConfig(config.config_file_name)
|
||||||
|
logger = logging.getLogger('alembic.env')
|
||||||
|
|
||||||
|
# add your model's MetaData object here
|
||||||
|
# for 'autogenerate' support
|
||||||
|
# from myapp import mymodel
|
||||||
|
# target_metadata = mymodel.Base.metadata
|
||||||
|
config.set_main_option(
|
||||||
|
'sqlalchemy.url',
|
||||||
|
str(current_app.extensions['migrate'].db.engine.url).replace('%', '%%'))
|
||||||
|
target_metadata = current_app.extensions['migrate'].db.metadata
|
||||||
|
|
||||||
|
# other values from the config, defined by the needs of env.py,
|
||||||
|
# can be acquired:
|
||||||
|
# my_important_option = config.get_main_option("my_important_option")
|
||||||
|
# ... etc.
|
||||||
|
|
||||||
|
|
||||||
|
def run_migrations_offline():
|
||||||
|
"""Run migrations in 'offline' mode.
|
||||||
|
|
||||||
|
This configures the context with just a URL
|
||||||
|
and not an Engine, though an Engine is acceptable
|
||||||
|
here as well. By skipping the Engine creation
|
||||||
|
we don't even need a DBAPI to be available.
|
||||||
|
|
||||||
|
Calls to context.execute() here emit the given string to the
|
||||||
|
script output.
|
||||||
|
|
||||||
|
"""
|
||||||
|
url = config.get_main_option("sqlalchemy.url")
|
||||||
|
context.configure(
|
||||||
|
url=url, target_metadata=target_metadata, literal_binds=True
|
||||||
|
)
|
||||||
|
|
||||||
|
with context.begin_transaction():
|
||||||
|
context.run_migrations()
|
||||||
|
|
||||||
|
|
||||||
|
def run_migrations_online():
|
||||||
|
"""Run migrations in 'online' mode.
|
||||||
|
|
||||||
|
In this scenario we need to create an Engine
|
||||||
|
and associate a connection with the context.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# this callback is used to prevent an auto-migration from being generated
|
||||||
|
# when there are no changes to the schema
|
||||||
|
# reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
|
||||||
|
def process_revision_directives(context, revision, directives):
|
||||||
|
if getattr(config.cmd_opts, 'autogenerate', False):
|
||||||
|
script = directives[0]
|
||||||
|
if script.upgrade_ops.is_empty():
|
||||||
|
directives[:] = []
|
||||||
|
logger.info('No changes in schema detected.')
|
||||||
|
|
||||||
|
connectable = current_app.extensions['migrate'].db.engine
|
||||||
|
|
||||||
|
with connectable.connect() as connection:
|
||||||
|
context.configure(
|
||||||
|
connection=connection,
|
||||||
|
target_metadata=target_metadata,
|
||||||
|
process_revision_directives=process_revision_directives,
|
||||||
|
**current_app.extensions['migrate'].configure_args
|
||||||
|
)
|
||||||
|
|
||||||
|
with context.begin_transaction():
|
||||||
|
context.run_migrations()
|
||||||
|
|
||||||
|
|
||||||
|
if context.is_offline_mode():
|
||||||
|
run_migrations_offline()
|
||||||
|
else:
|
||||||
|
run_migrations_online()
|
24
migrations/script.py.mako
Normal file
24
migrations/script.py.mako
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
"""${message}
|
||||||
|
|
||||||
|
Revision ID: ${up_revision}
|
||||||
|
Revises: ${down_revision | comma,n}
|
||||||
|
Create Date: ${create_date}
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
${imports if imports else ""}
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = ${repr(up_revision)}
|
||||||
|
down_revision = ${repr(down_revision)}
|
||||||
|
branch_labels = ${repr(branch_labels)}
|
||||||
|
depends_on = ${repr(depends_on)}
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
${upgrades if upgrades else "pass"}
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
${downgrades if downgrades else "pass"}
|
34
migrations/versions/be67c6934c05_pn_table.py
Normal file
34
migrations/versions/be67c6934c05_pn_table.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
"""PN table
|
||||||
|
|
||||||
|
Revision ID: be67c6934c05
|
||||||
|
Revises:
|
||||||
|
Create Date: 2021-05-03 17:01:39.845539
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'be67c6934c05'
|
||||||
|
down_revision = None
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('PN',
|
||||||
|
sa.Column('code', sa.String(length=3), nullable=False),
|
||||||
|
sa.Column('nom', sa.String(length=255), nullable=True),
|
||||||
|
sa.Column('diminutif', sa.String(length=30), nullable=True),
|
||||||
|
sa.Column('description', sa.Text(), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('code')
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_table('PN')
|
||||||
|
# ### end Alembic commands ###
|
28
migrations/versions/c1377d41bf27_pn_table.py
Normal file
28
migrations/versions/c1377d41bf27_pn_table.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
"""PN table
|
||||||
|
|
||||||
|
Revision ID: c1377d41bf27
|
||||||
|
Revises: be67c6934c05
|
||||||
|
Create Date: 2021-05-03 17:20:44.055359
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'c1377d41bf27'
|
||||||
|
down_revision = 'be67c6934c05'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('PN', sa.Column('type', sa.Integer(), nullable=True))
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_column('PN', 'type')
|
||||||
|
# ### end Alembic commands ###
|
Loading…
Reference in New Issue
Block a user