diff --git a/app/__init__.py b/app/__init__.py index 0943a91f6a..0ea6bb52c2 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -199,6 +199,10 @@ def create_app(config_class=DevConfig): app.register_blueprint(auth_bp, url_prefix="/auth") + from app.entreprises import bp as entreprises_bp + + app.register_blueprint(entreprises_bp, url_prefix="/ScoDoc/entreprises") + from app.views import scodoc_bp from app.views import scolar_bp from app.views import notes_bp diff --git a/app/entreprises/__init__.py b/app/entreprises/__init__.py new file mode 100644 index 0000000000..d9358508eb --- /dev/null +++ b/app/entreprises/__init__.py @@ -0,0 +1,25 @@ +"""entreprises.__init__ +""" + +from flask import Blueprint +from app.scodoc import sco_etud +from app.auth.models import User + +bp = Blueprint("entreprises", __name__) + +LOGS_LEN = 10 + +@bp.app_template_filter() +def format_prenom(s): + return sco_etud.format_prenom(s) + +@bp.app_template_filter() +def format_nom(s): + return sco_etud.format_nom(s) + +@bp.app_template_filter() +def get_nomcomplet(s): + user = User.query.filter_by(user_name=s).first() + return user.get_nomcomplet(); + +from app.entreprises import routes \ No newline at end of file diff --git a/app/entreprises/forms.py b/app/entreprises/forms.py new file mode 100644 index 0000000000..f7fd628168 --- /dev/null +++ b/app/entreprises/forms.py @@ -0,0 +1,120 @@ +from flask import flash +from markupsafe import Markup +from flask.app import Flask +import requests, re +from flask_wtf import FlaskForm +from wtforms import StringField, SubmitField, TextAreaField, SelectField, FileField +from wtforms.fields.html5 import EmailField, DateField +from wtforms.validators import ValidationError, DataRequired, Email +from app.entreprises.models import Entreprise +from app.models import Identite +from app.auth.models import User +from app.scodoc import sco_etud +from sqlalchemy import text + +DATA_REQUIRED_ERROR_MESSAGE = "Ce champ est requis" + +class EntrepriseCreationForm(FlaskForm): + siret = StringField("SIRET", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)], render_kw={"placeholder": "Numéro composé de 14 chiffres"}) + nom_entreprise = StringField("Nom de l'entreprise", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + adresse = StringField("Adresse de l'entreprise", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + codepostal = StringField("Code postal de l'entreprise", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + ville = StringField("Ville de l'entreprise", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + pays = StringField("Pays de l'entreprise", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + + nom_contact = StringField("Nom du contact", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + prenom_contact = StringField("Prénom du contact", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + telephone = StringField("Téléphone du contact", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + mail = EmailField("Mail du contact", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE), Email(message="Adresse e-mail invalide")]) + submit = SubmitField("Envoyer") + + def validate_siret(self, siret): + siret = siret.data.strip() + if re.match("^\d{14}$", siret) == None: + raise ValidationError("Format incorrect") + req = requests.get(f"https://entreprise.data.gouv.fr/api/sirene/v1/siret/{siret}") + if req.status_code != 200: + raise ValidationError("SIRET inexistant") + entreprise = Entreprise.query.filter_by(siret=siret).first() + if entreprise is not None: + lien = f"ici" + raise ValidationError(Markup(f"Entreprise déjà présent, lien vers la fiche : {lien}")) + +class EntrepriseModificationForm(FlaskForm): + siret = StringField("SIRET", validators=[], render_kw={"disabled":""}) + nom = StringField("Nom de l'entreprise", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + adresse = StringField("Adresse", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + codepostal = StringField("Code postal", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + ville = StringField("Ville", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + pays = StringField("Pays", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + submit = SubmitField("Modifier") + +class OffreCreationForm(FlaskForm): + intitule = StringField("Intitulé", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + description = TextAreaField("Description", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + type_offre = SelectField("Type de l'offre", choices=[('Stage'), ('Alternance')], validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + missions = TextAreaField("Missions", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + duree = StringField("Durée", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + ficher = FileField("Fichier", validators=[]) + submit = SubmitField("Envoyer") + +class OffreModificationForm(FlaskForm): + intitule = StringField("Intitulé", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + description = TextAreaField("Description", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + type_offre = SelectField("Type de l'offre", choices=[('Stage'), ('Alternance')], validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + missions = TextAreaField("Missions", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + duree = StringField("Durée", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + submit = SubmitField("Modifier") + +class ContactCreationForm(FlaskForm): + nom = StringField("Nom", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + prenom = StringField("Prénom", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + telephone = StringField("Téléphone", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + mail = EmailField("Mail", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE), Email(message="Adresse e-mail invalide")]) + submit = SubmitField("Envoyer") + +class ContactModificationForm(FlaskForm): + nom = StringField("Nom", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + prenom = StringField("Prénom", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + telephone = StringField("Téléphone", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + mail = EmailField("Mail", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE), Email(message="Adresse e-mail invalide")]) + submit = SubmitField("Modifier") + +class HistoriqueCreationForm(FlaskForm): + etudiant = StringField("Étudiant", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)], render_kw={"placeholder": "Tapez le nom de l'étudiant puis selectionnez"}) + type_offre = SelectField("Type de l'offre", choices=[('Stage'), ('Alternance')], validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + date_debut = DateField("Date début", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + date_fin = DateField("Date fin", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + submit = SubmitField("Envoyer") + + def validate(self): + rv = FlaskForm.validate(self) + if not rv: + return False + + if self.date_debut.data > self.date_fin.data: + self.date_debut.errors.append("Les dates sont incompatibles") + self.date_fin.errors.append("Les dates sont incompatibles") + return False + return True + + def validate_etudiant(self, etudiant): + etudiant_data = etudiant.data.upper().strip() + stm = text("SELECT id, CONCAT(nom, ' ', prenom) as nom_prenom FROM Identite WHERE CONCAT(nom, ' ', prenom)=:nom_prenom") + etudiant = Identite.query.from_statement(stm).params(nom_prenom=etudiant_data).first() + if etudiant is None: + raise ValidationError("Champ incorrect (selectionnez dans la liste)") + +class EnvoiOffreForm(FlaskForm): + responsable = StringField("Responsable de formation", validators=[DataRequired(message=DATA_REQUIRED_ERROR_MESSAGE)]) + submit = SubmitField("Envoyer") + + def validate_responsable(self, responsable): + responsable_data = responsable.data.upper().strip() + stm = text("SELECT id, UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')')) FROM \"user\" WHERE UPPER(CONCAT(nom, ' ', prenom, ' ', '(', user_name, ')'))=:responsable_data") + responsable = User.query.from_statement(stm).params(responsable_data=responsable_data).first() + if responsable is None: + raise ValidationError("Champ incorrect (selectionnez dans la liste)") + +class SuppressionConfirmationForm(FlaskForm): + submit = SubmitField("Supprimer") \ No newline at end of file diff --git a/app/entreprises/models.py b/app/entreprises/models.py new file mode 100644 index 0000000000..a079cacf8c --- /dev/null +++ b/app/entreprises/models.py @@ -0,0 +1,52 @@ +from app import db + +class Entreprise(db.Model): + __tablename__ = "entreprises" + id = db.Column(db.Integer, primary_key=True) + siret = db.Column(db.Text) + nom = db.Column(db.Text) + adresse = db.Column(db.Text) + codepostal = db.Column(db.Text) + ville = db.Column(db.Text) + pays = db.Column(db.Text) + contacts = db.relationship('EntrepriseContact', backref='entreprise', lazy='dynamic') + offres = db.relationship('EntrepriseOffre', backref='entreprise', lazy='dynamic') + +class EntrepriseContact(db.Model): + __tablename__ = "entreprise_contact" + id = db.Column(db.Integer, primary_key=True) + entreprise_id = db.Column(db.Integer, db.ForeignKey("entreprises.id")) + nom = db.Column(db.Text) + prenom = db.Column(db.Text) + telephone = db.Column(db.Text) + mail = db.Column(db.Text) + +class EntrepriseOffre(db.Model): + __tablename__ = "entreprise_offre" + id = db.Column(db.Integer, primary_key=True) + entreprise_id = db.Column(db.Integer, db.ForeignKey("entreprises.id")) + date_ajout = db.Column(db.DateTime(timezone=True), server_default=db.func.now()) + intitule = db.Column(db.Text) + description = db.Column(db.Text) + type_offre = db.Column(db.Text) + missions = db.Column(db.Text) + duree = db.Column(db.Text) + +class EntrepriseLog(db.Model): + __tablename__ = "entreprise_log" + id = db.Column(db.Integer, primary_key=True) + date = db.Column(db.DateTime(timezone=True), server_default=db.func.now()) + authenticated_user = db.Column(db.Text) + object = db.Column(db.Integer) + text = db.Column(db.Text) + +class EntrepriseHistory(db.Model): + __tablename__ = "entreprise_history" + id = db.Column(db.Integer, primary_key=True) + entreprise_id = db.Column(db.Integer, db.ForeignKey("entreprises.id")) + etudid = db.Column(db.Integer) + type_offre = db.Column(db.Text) + date_debut = db.Column(db.Date) + date_fin = db.Column(db.Date) + formation_text = db.Column(db.Text) + formation_scodoc = db.Column(db.Integer) diff --git a/app/entreprises/routes.py b/app/entreprises/routes.py new file mode 100644 index 0000000000..b74390873c --- /dev/null +++ b/app/entreprises/routes.py @@ -0,0 +1,374 @@ +from flask import render_template, redirect, url_for, request, flash +from flask.json import jsonify +from flask_login import current_user +from app.decorators import permission_required +from app.entreprises import LOGS_LEN +from app.scodoc.sco_permissions import Permission +from app.entreprises.forms import ( + EntrepriseCreationForm, + EntrepriseModificationForm, + SuppressionConfirmationForm, + OffreCreationForm, + OffreModificationForm, + ContactCreationForm, + ContactModificationForm, + HistoriqueCreationForm, + EnvoiOffreForm +) +from app.entreprises import bp +from app.entreprises.models import ( + Entreprise, + EntrepriseOffre, + EntrepriseContact, + EntrepriseLog, + EntrepriseHistory +) +from app.models import ( + Identite +) +from app.auth.models import User +from app.scodoc.sco_find_etud import search_etud_by_name +from app import db +from app.scodoc import sco_etud +from sqlalchemy import text + +@bp.route("/", methods=["GET", "POST"]) +def index(): + entreprises = Entreprise.query.all() + logs = EntrepriseLog.query.order_by(EntrepriseLog.date.desc()).limit(LOGS_LEN).all() + return render_template("entreprises/entreprises.html", title=("Entreprises"), entreprises=entreprises, logs=logs) + +@bp.route("/add_entreprise", methods=["GET", "POST"]) +def add_entreprise(): + form = EntrepriseCreationForm() + if form.validate_on_submit(): + entreprise = Entreprise( + nom=form.nom_entreprise.data.strip(), + siret=form.siret.data.strip(), + adresse=form.adresse.data.strip(), + codepostal=form.codepostal.data.strip(), + ville=form.ville.data.strip(), + pays=form.pays.data.strip() + ) + db.session.add(entreprise) + db.session.commit() + db.session.refresh(entreprise) + contact = EntrepriseContact( + entreprise_id=entreprise.id, + nom=form.nom_contact.data.strip(), + prenom=form.prenom_contact.data.strip(), + telephone=form.telephone.data.strip(), + mail=form.mail.data.strip() + ) + db.session.add(contact) + nom_entreprise = f"{entreprise.nom}" + log = EntrepriseLog( + authenticated_user = current_user.user_name, + text = f"{nom_entreprise} - Création de la fiche entreprise ({entreprise.nom}) avec un contact", + ) + db.session.add(log) + db.session.commit() + flash("L'entreprise a été ajouté à la liste.") + return redirect(url_for("entreprises.index")) + return render_template("entreprises/form.html", title=("Ajout entreprise + contact"), form=form) + +@bp.route("/edit_entreprise/", methods=["GET", "POST"]) +def edit_entreprise(id): + entreprise = Entreprise.query.filter_by(id=id).first_or_404() + form = EntrepriseModificationForm() + if form.validate_on_submit(): + nom_entreprise = f"{form.nom.data.strip()}" + if entreprise.nom != form.nom.data.strip(): + log = EntrepriseLog( + authenticated_user = current_user.user_name, + object = entreprise.id, + text = f"{nom_entreprise} - Modification du nom (ancien nom : {entreprise.nom})", + ) + entreprise.nom = form.nom.data.strip() + db.session.add(log) + if entreprise.adresse != form.adresse.data.strip(): + log = EntrepriseLog( + authenticated_user = current_user.user_name, + object = entreprise.id, + text = f"{nom_entreprise} - Modification de l'adresse (ancienne adresse : {entreprise.adresse})", + ) + entreprise.adresse = form.adresse.data.strip() + db.session.add(log) + if entreprise.codepostal != form.codepostal.data.strip(): + log = EntrepriseLog( + authenticated_user = current_user.user_name, + object = entreprise.id, + text = f"{nom_entreprise} - Modification du code postal (ancien code postal : {entreprise.codepostal})", + ) + entreprise.codepostal = form.codepostal.data.strip() + db.session.add(log) + if entreprise.ville != form.ville.data.strip(): + log = EntrepriseLog( + authenticated_user = current_user.user_name, + object = entreprise.id, + text = f"{nom_entreprise} - Modification de la ville (ancienne ville : {entreprise.ville})", + ) + entreprise.ville = form.ville.data.strip() + db.session.add(log) + if entreprise.pays != form.pays.data.strip(): + log = EntrepriseLog( + authenticated_user = current_user.user_name, + object = entreprise.id, + text = f"{nom_entreprise} - Modification du pays (ancien pays : {entreprise.pays})", + ) + entreprise.pays = form.pays.data.strip() + db.session.add(log) + db.session.commit() + flash("L'entreprise a été modifié.") + return redirect(url_for("entreprises.fiche_entreprise", id=entreprise.id)) + elif request.method == 'GET': + form.siret.data = entreprise.siret + form.nom.data = entreprise.nom + form.adresse.data = entreprise.adresse + form.codepostal.data = entreprise.codepostal + form.ville.data = entreprise.ville + form.pays.data = entreprise.pays + return render_template("entreprises/form.html", title=("Modification entreprise"), form=form) + +@bp.route("/delete_entreprise/", methods=["GET", "POST"]) +def delete_entreprise(id): + entreprise = Entreprise.query.filter_by(id=id).first_or_404() + form = SuppressionConfirmationForm() + if form.validate_on_submit(): + db.session.delete(entreprise) + log = EntrepriseLog( + authenticated_user = current_user.user_name, + object = entreprise.id, + text = f"Suppression de la fiche entreprise ({entreprise.nom})", + ) + db.session.add(log) + db.session.commit() + flash("L'entreprise a été supprimé de la liste.") + return redirect(url_for("entreprises.index")) + return render_template("entreprises/delete_confirmation.html", title=("Supression entreprise"), form=form) + +@bp.route("/fiche_entreprise/", methods=["GET", "POST"]) +def fiche_entreprise(id): + entreprise = Entreprise.query.filter_by(id=id).first_or_404() + offres = entreprise.offres + contacts = entreprise.contacts + logs = EntrepriseLog.query.order_by(EntrepriseLog.date.desc()).filter_by(object=id).limit(LOGS_LEN).all() + historique = db.session.query(EntrepriseHistory, Identite).order_by(EntrepriseHistory.date_debut.desc()).\ + filter_by(entreprise_id=id).\ + join(Identite, Identite.id == EntrepriseHistory.etudid).all() + return render_template("entreprises/fiche_entreprise.html", title=("Fiche entreprise"), entreprise=entreprise, contacts=contacts, offres=offres, logs=logs, historique=historique) + +@bp.route("/add_offre/", methods=["GET", "POST"]) +def add_offre(id): + entreprise = Entreprise.query.filter_by(id=id).first_or_404() + form = OffreCreationForm() + if form.validate_on_submit(): + offre = EntrepriseOffre( + entreprise_id=entreprise.id, + intitule=form.intitule.data.strip(), + description=form.description.data.strip(), + type_offre=form.type_offre.data.strip(), + missions=form.missions.data.strip(), + duree=form.duree.data.strip() + ) + log = EntrepriseLog( + authenticated_user = current_user.user_name, + object = entreprise.id, + text = "Création d'une offre", + ) + db.session.add(log) + db.session.add(offre) + db.session.commit() + flash("L'offre a été ajouté à la fiche entreprise.") + return redirect(url_for("entreprises.fiche_entreprise", id=entreprise.id)) + return render_template("entreprises/form.html", title=("Ajout offre"), form=form) + +@bp.route("/edit_offre/", methods=["GET", "POST"]) +def edit_offre(id): + offre = EntrepriseOffre.query.filter_by(id=id).first_or_404() + form = OffreModificationForm() + if form.validate_on_submit(): + offre.intitule = form.intitule.data.strip() + offre.description = form.description.data.strip() + offre.type_offre = form.type_offre.data.strip() + offre.missions = form.missions.data.strip() + offre.duree = form.duree.data.strip() + log = EntrepriseLog( + authenticated_user = current_user.user_name, + object = offre.entreprise_id, + text = "Modification d'une offre", + ) + db.session.add(log) + db.session.commit() + flash("L'offre a été modifié.") + return redirect(url_for("entreprises.fiche_entreprise", id=offre.entreprise.id)) + elif request.method == 'GET': + form.intitule.data = offre.intitule + form.description.data = offre.description + form.type_offre.data = offre.type_offre + form.missions.data = offre.missions + form.duree.data = offre.duree + return render_template("entreprises/form.html", title=("Modification offre"), form=form) + +@bp.route("/delete_offre/", methods=["GET", "POST"]) +def delete_offre(id): + offre = EntrepriseOffre.query.filter_by(id=id).first_or_404() + entreprise_id = offre.entreprise.id + form = SuppressionConfirmationForm() + if form.validate_on_submit(): + db.session.delete(offre) + log = EntrepriseLog( + authenticated_user = current_user.user_name, + object = offre.entreprise_id, + text = "Suppression d'une offre", + ) + db.session.add(log) + db.session.commit() + flash("L'offre a été supprimé de la fiche entreprise.") + return redirect(url_for("entreprises.fiche_entreprise", id=entreprise_id)) + return render_template("entreprises/delete_confirmation.html", title=("Supression offre"), form=form) + +@bp.route("/add_contact/", methods=["GET", "POST"]) +def add_contact(id): + entreprise = Entreprise.query.filter_by(id=id).first_or_404() + form = ContactCreationForm() + if form.validate_on_submit(): + contact = EntrepriseContact( + entreprise_id=entreprise.id, + nom=form.nom.data.strip(), + prenom=form.prenom.data.strip(), + telephone=form.telephone.data.strip(), + mail=form.mail.data.strip() + ) + log = EntrepriseLog( + authenticated_user = current_user.user_name, + object = entreprise.id, + text = "Création d'un contact", + ) + db.session.add(log) + db.session.add(contact) + db.session.commit() + flash("Le contact a été ajouté à la fiche entreprise.") + return redirect(url_for("entreprises.fiche_entreprise", id=entreprise.id)) + return render_template("entreprises/form.html", title=("Ajout contact"), form=form) + +@bp.route("/edit_contact/", methods=["GET", "POST"]) +def edit_contact(id): + contact = EntrepriseContact.query.filter_by(id=id).first_or_404() + form = ContactModificationForm() + if form.validate_on_submit(): + contact.nom = form.nom.data.strip() + contact.prenom = form.prenom.data.strip() + contact.telephone = form.telephone.data.strip() + contact.mail = form.mail.data.strip() + log = EntrepriseLog( + authenticated_user = current_user.user_name, + object = contact.entreprise_id, + text = "Modification d'un contact", + ) + db.session.add(log) + db.session.commit() + flash("Le contact a été modifié.") + return redirect(url_for("entreprises.fiche_entreprise", id=contact.entreprise.id)) + elif request.method == 'GET': + form.nom.data = contact.nom + form.prenom.data = contact.prenom + form.telephone.data = contact.telephone + form.mail.data = contact.mail + return render_template("entreprises/form.html", title=("Modification contact"), form=form) + +@bp.route("/delete_contact/", methods=["GET", "POST"]) +def delete_contact(id): + contact = EntrepriseContact.query.filter_by(id=id).first_or_404() + entreprise_id = contact.entreprise.id + form = SuppressionConfirmationForm() + if form.validate_on_submit(): + contact_count = EntrepriseContact.query.filter_by(entreprise_id=contact.entreprise.id).count() + if contact_count == 1: + flash("Le contact n'a pas été supprimé de la fiche entreprise. (1 contact minimum)") + return redirect(url_for("entreprises.fiche_entreprise", id=entreprise_id)) + else: + db.session.delete(contact) + log = EntrepriseLog( + authenticated_user = current_user.user_name, + object = contact.entreprise_id, + text = "Suppression d'un contact", + ) + db.session.add(log) + db.session.commit() + flash("Le contact a été supprimé de la fiche entreprise.") + return redirect(url_for("entreprises.fiche_entreprise", id=entreprise_id)) + return render_template("entreprises/delete_confirmation.html", title=("Supression contact"), form=form) + +@bp.route("/add_historique/", methods=["GET", "POST"]) +def add_historique(id): + entreprise = Entreprise.query.filter_by(id=id).first_or_404() + form = HistoriqueCreationForm() + if form.validate_on_submit(): + etudiant_nomcomplet = form.etudiant.data.upper().strip() + stm = text("SELECT id, CONCAT(nom, ' ', prenom) as nom_prenom FROM Identite WHERE CONCAT(nom, ' ', prenom)=:nom_prenom") + etudiant = Identite.query.from_statement(stm).params(nom_prenom=etudiant_nomcomplet).first() + formation = etudiant.inscription_courante_date(form.date_debut.data, form.date_fin.data) + historique = EntrepriseHistory( + entreprise_id = entreprise.id, + etudid = etudiant.id, + type_offre = form.type_offre.data.strip(), + date_debut = form.date_debut.data, + date_fin = form.date_fin.data, + formation_text = formation.formsemestre.titre + if formation else None, + formation_scodoc = formation.formsemestre.formsemestre_id + if formation else None + ) + db.session.add(historique) + db.session.commit() + flash("L'étudiant a été ajouté sur la fiche entreprise.") + return redirect(url_for("entreprises.fiche_entreprise", id=entreprise.id)) + return render_template("entreprises/form.html", title=("Ajout historique"), form=form) + +@bp.route("/etudiants") +def json_etudiants(): + term = request.args.get('term').strip() + etudiants = Identite.query.filter(Identite.nom.ilike(f"%{term}%")).all() + list = [] + content = {} + for etudiant in etudiants: + value = f"{sco_etud.format_nom(etudiant.nom)} {sco_etud.format_prenom(etudiant.prenom)}" + if etudiant.inscription_courante() is not None: + content = { + "id": f"{etudiant.id}", + "value": value, + "info": f"{etudiant.inscription_courante().formsemestre.titre}" + } + else: + content = { + "id": f"{etudiant.id}", + "value": value + } + list.append(content) + content = {} + return jsonify(results=list) + +@bp.route("/envoyer_offre/", methods=["GET", "POST"]) +def envoyer_offre(id): + form = EnvoiOffreForm() + if form.validate_on_submit(): + print("tmp") # faire l'envoie du mail + return render_template("entreprises/form.html", title=("Envoyer une offre"), form=form) + +@bp.route("/responsables") +def json_responsables(): + term = request.args.get('term').strip() + responsables = User.query.filter(User.nom.ilike(f"%{term}%"), User.nom.is_not(None), User.prenom.is_not(None)).all() + list = [] + content = {} + for responsable in responsables: + value = f"{responsable.get_nomplogin()}" + content = { + "id": f"{responsable.id}", + "value": value, + "info": "" + } + list.append(content) + content = {} + return jsonify(results=list) \ No newline at end of file diff --git a/app/models/__init__.py b/app/models/__init__.py index 0fee7bc48e..642e31873e 100644 --- a/app/models/__init__.py +++ b/app/models/__init__.py @@ -14,12 +14,6 @@ from app.models.raw_sql_init import create_database_functions from app.models.absences import Absence, AbsenceNotification, BilletAbsence from app.models.departements import Departement - -from app.models.entreprises import ( - Entreprise, - EntrepriseCorrespondant, - EntrepriseContact, -) from app.models.etudiants import ( Identite, Adresse, diff --git a/app/models/departements.py b/app/models/departements.py index 0734e35b0d..7ed2f4b56e 100644 --- a/app/models/departements.py +++ b/app/models/departements.py @@ -19,7 +19,7 @@ class Departement(db.Model): db.Boolean(), nullable=False, default=True, server_default="true" ) # sur page d'accueil - entreprises = db.relationship("Entreprise", lazy="dynamic", backref="departement") + # entreprises = db.relationship("Entreprise", lazy="dynamic", backref="departement") etudiants = db.relationship("Identite", lazy="dynamic", backref="departement") formations = db.relationship("Formation", lazy="dynamic", backref="departement") formsemestres = db.relationship( diff --git a/app/models/entreprises.py b/app/models/entreprises.py deleted file mode 100644 index bdb5672a8a..0000000000 --- a/app/models/entreprises.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- coding: UTF-8 -* - -"""Gestion des absences -""" - -from app import db -from app.models import APO_CODE_STR_LEN -from app.models import SHORT_STR_LEN -from app.models import CODE_STR_LEN - - -class Entreprise(db.Model): - """une entreprise""" - - __tablename__ = "entreprises" - id = db.Column(db.Integer, primary_key=True) - entreprise_id = db.synonym("id") - dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"), index=True) - nom = db.Column(db.Text) - adresse = db.Column(db.Text) - ville = db.Column(db.Text) - codepostal = db.Column(db.Text) - pays = db.Column(db.Text) - contact_origine = db.Column(db.Text) - secteur = db.Column(db.Text) - note = db.Column(db.Text) - privee = db.Column(db.Text) - localisation = db.Column(db.Text) - # -1 inconnue, 0, 25, 50, 75, 100: - qualite_relation = db.Column(db.Integer) - plus10salaries = db.Column(db.Boolean()) - date_creation = db.Column(db.DateTime(timezone=True), server_default=db.func.now()) - - -class EntrepriseCorrespondant(db.Model): - """Personne contact en entreprise""" - - __tablename__ = "entreprise_correspondant" - id = db.Column(db.Integer, primary_key=True) - entreprise_corresp_id = db.synonym("id") - entreprise_id = db.Column(db.Integer, db.ForeignKey("entreprises.id")) - nom = db.Column(db.Text) - prenom = db.Column(db.Text) - civilite = db.Column(db.Text) - fonction = db.Column(db.Text) - phone1 = db.Column(db.Text) - phone2 = db.Column(db.Text) - mobile = db.Column(db.Text) - mail1 = db.Column(db.Text) - mail2 = db.Column(db.Text) - fax = db.Column(db.Text) - note = db.Column(db.Text) - - -class EntrepriseContact(db.Model): - """Evènement (contact) avec une entreprise""" - - __tablename__ = "entreprise_contact" - id = db.Column(db.Integer, primary_key=True) - entreprise_contact_id = db.synonym("id") - date = db.Column(db.DateTime(timezone=True)) - type_contact = db.Column(db.Text) - entreprise_id = db.Column(db.Integer, db.ForeignKey("entreprises.id")) - entreprise_corresp_id = db.Column( - db.Integer, db.ForeignKey("entreprise_correspondant.id") - ) - etudid = db.Column(db.Integer) # sans contrainte pour garder logs après suppression - description = db.Column(db.Text) - enseignant = db.Column(db.Text) diff --git a/app/models/etudiants.py b/app/models/etudiants.py index 0ae36bd28e..0f81ad6bf0 100644 --- a/app/models/etudiants.py +++ b/app/models/etudiants.py @@ -107,6 +107,14 @@ class Identite(db.Model): ] return r[0] if r else None + def inscription_courante_date(self, date_debut, date_fin): + r = [ + ins + for ins in self.formsemestre_inscriptions + if ins.formsemestre.est_courant_date(date_debut, date_fin) + ] + return r[0] if r else None + def etat_inscription(self, formsemestre_id): """etat de l'inscription de cet étudiant au semestre: False si pas inscrit, ou scu.INSCRIT, DEMISSION, DEF diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index 450ea2ccfb..dc13c1cc8c 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -146,6 +146,12 @@ class FormSemestre(db.Model): today = datetime.date.today() return (self.date_debut <= today) and (today <= self.date_fin) + def est_courant_date(self, date_debut, date_fin) -> bool: + """Vrai si la date actuelle (now) est dans le semestre + (les dates de début et fin sont incluses) + """ + return (self.date_debut <= date_debut) and (date_fin <= self.date_fin) + def est_decale(self): """Vrai si semestre "décalé" c'est à dire semestres impairs commençant entre janvier et juin diff --git a/app/templates/entreprises/_contact.html b/app/templates/entreprises/_contact.html new file mode 100644 index 0000000000..eff3a8cb22 --- /dev/null +++ b/app/templates/entreprises/_contact.html @@ -0,0 +1,13 @@ +
+

+ Nom : {{ contact.nom }}
+ Prénom : {{ contact.prenom }}
+ Téléphone : {{ contact.telephone }}
+ Mail : {{ contact.mail }}
+

+ + +
\ No newline at end of file diff --git a/app/templates/entreprises/_offre.html b/app/templates/entreprises/_offre.html new file mode 100644 index 0000000000..e64f3c16ce --- /dev/null +++ b/app/templates/entreprises/_offre.html @@ -0,0 +1,15 @@ +
+

+ Intitulé : {{ offre.intitule }}
+ Description : {{ offre.description }}
+ Type de l'offre : {{ offre.type_offre }}
+ Missions : {{ offre.missions }}
+ Durée : {{ offre.duree }}
+

+ + +
\ No newline at end of file diff --git a/app/templates/entreprises/delete_confirmation.html b/app/templates/entreprises/delete_confirmation.html new file mode 100644 index 0000000000..06b75b6ad8 --- /dev/null +++ b/app/templates/entreprises/delete_confirmation.html @@ -0,0 +1,14 @@ +{% extends 'base.html' %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block app_content %} +

{{ title }}

+
+
Cliquez sur le bouton supprimer pour confirmer votre supression
+
+
+
+ {{ wtf.quick_form(form) }} +
+
+{% endblock %} \ No newline at end of file diff --git a/app/templates/entreprises/entreprises.html b/app/templates/entreprises/entreprises.html new file mode 100644 index 0000000000..4ca2860eea --- /dev/null +++ b/app/templates/entreprises/entreprises.html @@ -0,0 +1,65 @@ +{% extends 'base.html' %} + +{% block app_content %} + + {% if logs %} +
+

Dernières opérations

+
    + {% for log in logs %} +
  • {{ log.date.strftime('%d %b %Hh%M') }}{{ log.text|safe }} par {{ log.authenticated_user|get_nomcomplet }}
  • + {% endfor %} +
+
+ {% endif %} +
+

Liste des entreprises

+ {% if entreprises %} +
+ + + + + + + + + + + {% for entreprise in entreprises %} + + + + + + + + + + {% endfor %} +
SIRETNomAdresseCode postalVillePaysAction
{{ entreprise.siret }}{{ entreprise.nom }}{{ entreprise.adresse }}{{ entreprise.codepostal }}{{ entreprise.ville }}{{ entreprise.pays }} + +
+
+ {% else %} +
Aucune entreprise présent dans la base
+
+
+ {% endif %} + Ajouter une entreprise +
+{% endblock %} \ No newline at end of file diff --git a/app/templates/entreprises/fiche_entreprise.html b/app/templates/entreprises/fiche_entreprise.html new file mode 100644 index 0000000000..5667d1328c --- /dev/null +++ b/app/templates/entreprises/fiche_entreprise.html @@ -0,0 +1,70 @@ +{% extends 'base.html' %} + +{% block app_content %} + {% if logs %} +
+

Dernières opérations sur cette fiche

+
    + {% for log in logs %} +
  • + {{ log.date.strftime('%d %b %Hh%M') }} + {{ log.text|safe }} par {{ log.authenticated_user|get_nomcomplet }} +
  • + {% endfor %} +
+
+ {% endif %} + {% if historique %} +
+

Historique

+
    + {% for data in historique %} +
  • + {{ data[0].date_debut.strftime('%d/%m/%Y') }} - {{ data[0].date_fin.strftime('%d/%m/%Y') }} + {{ data[0].type_offre }} réalisé par {{ data[1].nom|format_nom }} {{ data[1].prenom|format_prenom }} +
  • + {% endfor %} +
+
+ {% endif %} +
+

Fiche entreprise - {{ entreprise.nom }} ({{ entreprise.siret }})

+ +
+

+ SIRET : {{ entreprise.siret }}
+ Nom : {{ entreprise.nom }}
+ Adresse : {{ entreprise.adresse }}
+ Code postal : {{ entreprise.codepostal }}
+ Ville : {{ entreprise.ville }}
+ Pays : {{ entreprise.pays }} +

+
+ + {% if contacts %} +
+ {% for contact in contacts %} + Contact {{loop.index}} + {% include 'entreprises/_contact.html' %} + {% endfor %} +
+ {% endif %} + + {% if offres %} +
+ {% for offre in offres %} + Offre {{loop.index}} (ajouté le {{offre.date_ajout.strftime('%d/%m/%Y') }}) + {% include 'entreprises/_offre.html' %} + {% endfor %} +
+ {% endif %} + + +
+{% endblock %} \ No newline at end of file diff --git a/app/templates/entreprises/form.html b/app/templates/entreprises/form.html new file mode 100644 index 0000000000..2efa5aedc9 --- /dev/null +++ b/app/templates/entreprises/form.html @@ -0,0 +1,71 @@ +{% extends 'base.html' %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block styles %} +{{super()}} + + +{% endblock %} + +{% block app_content %} +

{{ title }}

+
+
+
+ {{ wtf.quick_form(form, novalidate=True) }} +
+
+ +{% endblock %} \ No newline at end of file diff --git a/migrations/versions/f62d3a0bde1a_creation_tables_relations_entreprises.py b/migrations/versions/f62d3a0bde1a_creation_tables_relations_entreprises.py new file mode 100644 index 0000000000..ae414038bb --- /dev/null +++ b/migrations/versions/f62d3a0bde1a_creation_tables_relations_entreprises.py @@ -0,0 +1,127 @@ +"""creation tables relations entreprises + +Revision ID: f62d3a0bde1a +Revises: 39818df276aa +Create Date: 2021-12-10 11:25:04.135491 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'f62d3a0bde1a' +down_revision = '91be8a06d423' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('entreprise_log', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('date', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), + sa.Column('authenticated_user', sa.Text(), nullable=True), + sa.Column('object', sa.Integer(), nullable=True), + sa.Column('text', sa.Text(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('entreprise_history', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('entreprise_id', sa.Integer(), nullable=True), + sa.Column('etudid', sa.Integer(), nullable=True), + sa.Column('type_offre', sa.Text(), nullable=True), + sa.Column('date_debut', sa.Date(), nullable=True), + sa.Column('date_fin', sa.Date(), nullable=True), + sa.Column('formation_text', sa.Text(), nullable=True), + sa.Column('formation_scodoc', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['entreprise_id'], ['entreprises.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('entreprise_offre', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('entreprise_id', sa.Integer(), nullable=True), + sa.Column('date_ajout', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=True), + sa.Column('intitule', sa.Text(), nullable=True), + sa.Column('description', sa.Text(), nullable=True), + sa.Column('type_offre', sa.Text(), nullable=True), + sa.Column('missions', sa.Text(), nullable=True), + sa.Column('duree', sa.Text(), nullable=True), + sa.ForeignKeyConstraint(['entreprise_id'], ['entreprises.id'], ), + sa.PrimaryKeyConstraint('id') + ) + + op.drop_constraint('entreprise_contact_entreprise_corresp_id_fkey', 'entreprise_contact', type_='foreignkey') + op.drop_table('entreprise_correspondant') + op.add_column('entreprise_contact', sa.Column('nom', sa.Text(), nullable=True)) + op.add_column('entreprise_contact', sa.Column('prenom', sa.Text(), nullable=True)) + op.add_column('entreprise_contact', sa.Column('telephone', sa.Text(), nullable=True)) + op.add_column('entreprise_contact', sa.Column('mail', sa.Text(), nullable=True)) + op.drop_column('entreprise_contact', 'date') + op.drop_column('entreprise_contact', 'description') + op.drop_column('entreprise_contact', 'type_contact') + op.drop_column('entreprise_contact', 'enseignant') + op.drop_column('entreprise_contact', 'entreprise_corresp_id') + op.drop_column('entreprise_contact', 'etudid') + + op.add_column('entreprises', sa.Column('siret', sa.Text(), nullable=True)) + op.drop_index('ix_entreprises_dept_id', table_name='entreprises') + op.drop_constraint('entreprises_dept_id_fkey', 'entreprises', type_='foreignkey') + op.drop_column('entreprises', 'localisation') + op.drop_column('entreprises', 'contact_origine') + op.drop_column('entreprises', 'qualite_relation') + op.drop_column('entreprises', 'secteur') + op.drop_column('entreprises', 'plus10salaries') + op.drop_column('entreprises', 'dept_id') + op.drop_column('entreprises', 'privee') + op.drop_column('entreprises', 'date_creation') + op.drop_column('entreprises', 'note') + # ### end Alembic commands ### + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('entreprises', sa.Column('note', sa.TEXT(), autoincrement=False, nullable=True)) + op.add_column('entreprises', sa.Column('date_creation', postgresql.TIMESTAMP(timezone=True), server_default=sa.text('now()'), autoincrement=False, nullable=True)) + op.add_column('entreprises', sa.Column('privee', sa.TEXT(), autoincrement=False, nullable=True)) + op.add_column('entreprises', sa.Column('dept_id', sa.INTEGER(), autoincrement=False, nullable=True)) + op.add_column('entreprises', sa.Column('plus10salaries', sa.BOOLEAN(), autoincrement=False, nullable=True)) + op.add_column('entreprises', sa.Column('secteur', sa.TEXT(), autoincrement=False, nullable=True)) + op.add_column('entreprises', sa.Column('qualite_relation', sa.INTEGER(), autoincrement=False, nullable=True)) + op.add_column('entreprises', sa.Column('contact_origine', sa.TEXT(), autoincrement=False, nullable=True)) + op.add_column('entreprises', sa.Column('localisation', sa.TEXT(), autoincrement=False, nullable=True)) + op.create_foreign_key('entreprises_dept_id_fkey', 'entreprises', 'departement', ['dept_id'], ['id']) + op.create_index('ix_entreprises_dept_id', 'entreprises', ['dept_id'], unique=False) + op.drop_column('entreprises', 'siret') + op.add_column('entreprise_contact', sa.Column('etudid', sa.INTEGER(), autoincrement=False, nullable=True)) + op.add_column('entreprise_contact', sa.Column('entreprise_corresp_id', sa.INTEGER(), autoincrement=False, nullable=True)) + op.add_column('entreprise_contact', sa.Column('enseignant', sa.TEXT(), autoincrement=False, nullable=True)) + op.add_column('entreprise_contact', sa.Column('type_contact', sa.TEXT(), autoincrement=False, nullable=True)) + op.add_column('entreprise_contact', sa.Column('description', sa.TEXT(), autoincrement=False, nullable=True)) + op.add_column('entreprise_contact', sa.Column('date', postgresql.TIMESTAMP(timezone=True), autoincrement=False, nullable=True)) + op.create_table('entreprise_correspondant', + sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False), + sa.Column('entreprise_id', sa.INTEGER(), autoincrement=False, nullable=True), + sa.Column('nom', sa.TEXT(), autoincrement=False, nullable=True), + sa.Column('prenom', sa.TEXT(), autoincrement=False, nullable=True), + sa.Column('civilite', sa.TEXT(), autoincrement=False, nullable=True), + sa.Column('fonction', sa.TEXT(), autoincrement=False, nullable=True), + sa.Column('phone1', sa.TEXT(), autoincrement=False, nullable=True), + sa.Column('phone2', sa.TEXT(), autoincrement=False, nullable=True), + sa.Column('mobile', sa.TEXT(), autoincrement=False, nullable=True), + sa.Column('mail1', sa.TEXT(), autoincrement=False, nullable=True), + sa.Column('mail2', sa.TEXT(), autoincrement=False, nullable=True), + sa.Column('fax', sa.TEXT(), autoincrement=False, nullable=True), + sa.Column('note', sa.TEXT(), autoincrement=False, nullable=True), + sa.ForeignKeyConstraint(['entreprise_id'], ['entreprises.id'], name='entreprise_correspondant_entreprise_id_fkey'), + sa.PrimaryKeyConstraint('id', name='entreprise_correspondant_pkey') + ) + op.create_foreign_key('entreprise_contact_entreprise_corresp_id_fkey', 'entreprise_contact', 'entreprise_correspondant', ['entreprise_corresp_id'], ['id']) + op.drop_column('entreprise_contact', 'mail') + op.drop_column('entreprise_contact', 'telephone') + op.drop_column('entreprise_contact', 'prenom') + op.drop_column('entreprise_contact', 'nom') + + op.drop_table('entreprise_offre') + op.drop_table('entreprise_history') + op.drop_table('entreprise_log') + # ### end Alembic commands ### \ No newline at end of file