diff --git a/app/__init__.py b/app/__init__.py index 0943a91f6..0ea6bb52c 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 000000000..44968ffb1 --- /dev/null +++ b/app/entreprises/__init__.py @@ -0,0 +1,29 @@ +"""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 diff --git a/app/entreprises/forms.py b/app/entreprises/forms.py new file mode 100644 index 000000000..0e145f43d --- /dev/null +++ b/app/entreprises/forms.py @@ -0,0 +1,262 @@ +from flask import flash +from flask_wtf import FlaskForm +from markupsafe import Markup +import requests, re +from wtforms import StringField, SubmitField, TextAreaField, SelectField, HiddenField +from wtforms.fields.html5 import EmailField, DateField +from flask_wtf.file import FileField, FileAllowed, FileRequired +from wtforms.validators import ValidationError, DataRequired, Email +from app.entreprises.models import Entreprise, EntrepriseContact +from app.models import Identite +from app.auth.models import User +from app.scodoc import sco_etud +from sqlalchemy import text + +CHAMP_REQUIS = "Ce champ est requis" + + +class EntrepriseCreationForm(FlaskForm): + siret = StringField( + "SIRET", + validators=[DataRequired(message=CHAMP_REQUIS)], + render_kw={"placeholder": "Numéro composé de 14 chiffres", "maxlength": "14"}, + ) + nom_entreprise = StringField( + "Nom de l'entreprise", + validators=[DataRequired(message=CHAMP_REQUIS)], + ) + adresse = StringField( + "Adresse de l'entreprise", + validators=[DataRequired(message=CHAMP_REQUIS)], + ) + codepostal = StringField( + "Code postal de l'entreprise", + validators=[DataRequired(message=CHAMP_REQUIS)], + ) + ville = StringField( + "Ville de l'entreprise", + validators=[DataRequired(message=CHAMP_REQUIS)], + ) + pays = StringField( + "Pays de l'entreprise", + validators=[DataRequired(message=CHAMP_REQUIS)], + render_kw={"style": "margin-bottom: 50px;"}, + ) + + nom_contact = StringField( + "Nom du contact", validators=[DataRequired(message=CHAMP_REQUIS)] + ) + prenom_contact = StringField( + "Prénom du contact", + validators=[DataRequired(message=CHAMP_REQUIS)], + ) + telephone = StringField( + "Téléphone du contact", + validators=[DataRequired(message=CHAMP_REQUIS)], + ) + mail = EmailField( + "Mail du contact", + validators=[ + DataRequired(message=CHAMP_REQUIS), + Email(message="Adresse e-mail invalide"), + ], + ) + poste = StringField("Poste du contact", validators=[]) + service = StringField("Service du contact", validators=[]) + submit = SubmitField("Envoyer", render_kw={"style": "margin-bottom: 10px;"}) + + 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=CHAMP_REQUIS)], + ) + adresse = StringField("Adresse", validators=[DataRequired(message=CHAMP_REQUIS)]) + codepostal = StringField( + "Code postal", validators=[DataRequired(message=CHAMP_REQUIS)] + ) + ville = StringField("Ville", validators=[DataRequired(message=CHAMP_REQUIS)]) + pays = StringField("Pays", validators=[DataRequired(message=CHAMP_REQUIS)]) + submit = SubmitField("Modifier", render_kw={"style": "margin-bottom: 10px;"}) + + +class OffreCreationForm(FlaskForm): + intitule = StringField("Intitulé", validators=[DataRequired(message=CHAMP_REQUIS)]) + description = TextAreaField( + "Description", validators=[DataRequired(message=CHAMP_REQUIS)] + ) + type_offre = SelectField( + "Type de l'offre", + choices=[("Stage"), ("Alternance")], + validators=[DataRequired(message=CHAMP_REQUIS)], + ) + missions = TextAreaField( + "Missions", validators=[DataRequired(message=CHAMP_REQUIS)] + ) + duree = StringField("Durée", validators=[DataRequired(message=CHAMP_REQUIS)]) + submit = SubmitField("Envoyer", render_kw={"style": "margin-bottom: 10px;"}) + + +class OffreModificationForm(FlaskForm): + intitule = StringField("Intitulé", validators=[DataRequired(message=CHAMP_REQUIS)]) + description = TextAreaField( + "Description", validators=[DataRequired(message=CHAMP_REQUIS)] + ) + type_offre = SelectField( + "Type de l'offre", + choices=[("Stage"), ("Alternance")], + validators=[DataRequired(message=CHAMP_REQUIS)], + ) + missions = TextAreaField( + "Missions", validators=[DataRequired(message=CHAMP_REQUIS)] + ) + duree = StringField("Durée", validators=[DataRequired(message=CHAMP_REQUIS)]) + submit = SubmitField("Modifier", render_kw={"style": "margin-bottom: 10px;"}) + + +class ContactCreationForm(FlaskForm): + hidden_entreprise_id = HiddenField() + nom = StringField("Nom", validators=[DataRequired(message=CHAMP_REQUIS)]) + prenom = StringField("Prénom", validators=[DataRequired(message=CHAMP_REQUIS)]) + telephone = StringField( + "Téléphone", validators=[DataRequired(message=CHAMP_REQUIS)] + ) + mail = EmailField( + "Mail", + validators=[ + DataRequired(message=CHAMP_REQUIS), + Email(message="Adresse e-mail invalide"), + ], + ) + poste = StringField("Poste", validators=[]) + service = StringField("Service", validators=[]) + submit = SubmitField("Envoyer", render_kw={"style": "margin-bottom: 10px;"}) + + def validate(self): + rv = FlaskForm.validate(self) + if not rv: + return False + + contact = EntrepriseContact.query.filter_by( + entreprise_id=self.hidden_entreprise_id.data, + nom=self.nom.data, + prenom=self.prenom.data, + ).first() + + if contact is not None: + self.nom.errors.append("Ce contact existe déjà (même nom et prénom)") + self.prenom.errors.append("") + return False + + return True + + +class ContactModificationForm(FlaskForm): + nom = StringField("Nom", validators=[DataRequired(message=CHAMP_REQUIS)]) + prenom = StringField("Prénom", validators=[DataRequired(message=CHAMP_REQUIS)]) + telephone = StringField( + "Téléphone", validators=[DataRequired(message=CHAMP_REQUIS)] + ) + mail = EmailField( + "Mail", + validators=[ + DataRequired(message=CHAMP_REQUIS), + Email(message="Adresse e-mail invalide"), + ], + ) + poste = StringField("Poste", validators=[]) + service = StringField("Service", validators=[]) + submit = SubmitField("Modifier", render_kw={"style": "margin-bottom: 10px;"}) + + +class HistoriqueCreationForm(FlaskForm): + etudiant = StringField( + "Étudiant", + validators=[DataRequired(message=CHAMP_REQUIS)], + render_kw={"placeholder": "Tapez le nom de l'étudiant puis selectionnez"}, + ) + type_offre = SelectField( + "Type de l'offre", + choices=[("Stage"), ("Alternance")], + validators=[DataRequired(message=CHAMP_REQUIS)], + ) + date_debut = DateField( + "Date début", validators=[DataRequired(message=CHAMP_REQUIS)] + ) + date_fin = DateField("Date fin", validators=[DataRequired(message=CHAMP_REQUIS)]) + submit = SubmitField("Envoyer", render_kw={"style": "margin-bottom: 10px;"}) + + 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=CHAMP_REQUIS)], + ) + submit = SubmitField("Envoyer", render_kw={"style": "margin-bottom: 10px;"}) + + 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 AjoutFichierForm(FlaskForm): + fichier = FileField( + "Fichier", + validators=[ + FileRequired(message=CHAMP_REQUIS), + FileAllowed(["pdf", "docx"], "Fichier .pdf ou .docx uniquement"), + ], + ) + submit = SubmitField("Envoyer", render_kw={"style": "margin-bottom: 10px;"}) + + +class SuppressionConfirmationForm(FlaskForm): + submit = SubmitField("Supprimer", render_kw={"style": "margin-bottom: 10px;"}) diff --git a/app/entreprises/models.py b/app/entreprises/models.py new file mode 100644 index 000000000..b0cd25de7 --- /dev/null +++ b/app/entreprises/models.py @@ -0,0 +1,102 @@ +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", + cascade="all, delete-orphan", + ) + offres = db.relationship( + "EntrepriseOffre", + backref="entreprise", + lazy="dynamic", + cascade="all, delete-orphan", + ) + + def to_dict(self): + return { + "siret": self.siret, + "nom": self.nom, + "adresse": self.adresse, + "codepostal": self.codepostal, + "ville": self.ville, + "pays": self.pays, + } + + +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", ondelete="cascade") + ) + nom = db.Column(db.Text) + prenom = db.Column(db.Text) + telephone = db.Column(db.Text) + mail = db.Column(db.Text) + poste = db.Column(db.Text) + service = db.Column(db.Text) + + def to_dict(self): + return { + "nom": self.nom, + "prenom": self.prenom, + "telephone": self.telephone, + "mail": self.mail, + "poste": self.poste, + "service": self.service, + } + + +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", ondelete="cascade") + ) + 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 EntrepriseEtudiant(db.Model): + __tablename__ = "entreprise_etudiant" + 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) + + +class EntrepriseEnvoiOffre(db.Model): + __tablename__ = "entreprise_envoi_offre" + id = db.Column(db.Integer, primary_key=True) + sender_id = db.Column(db.Integer, db.ForeignKey("user.id")) + receiver_id = db.Column(db.Integer, db.ForeignKey("user.id")) + offre_id = db.Column(db.Integer, db.ForeignKey("entreprise_offre.id")) + date_envoi = db.Column(db.DateTime(timezone=True), server_default=db.func.now()) diff --git a/app/entreprises/routes.py b/app/entreprises/routes.py new file mode 100644 index 000000000..590dd313c --- /dev/null +++ b/app/entreprises/routes.py @@ -0,0 +1,630 @@ +import os +from config import Config +from datetime import datetime +import glob +import shutil + +from flask import render_template, redirect, url_for, request, flash, send_file, abort +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.entreprises.forms import ( + EntrepriseCreationForm, + EntrepriseModificationForm, + SuppressionConfirmationForm, + OffreCreationForm, + OffreModificationForm, + ContactCreationForm, + ContactModificationForm, + HistoriqueCreationForm, + EnvoiOffreForm, + AjoutFichierForm, +) +from app.entreprises import bp +from app.entreprises.models import ( + Entreprise, + EntrepriseOffre, + EntrepriseContact, + EntrepriseLog, + EntrepriseEtudiant, + EntrepriseEnvoiOffre, +) +from app.models import Identite +from app.auth.models import User +from app.scodoc.sco_permissions import Permission +from app.scodoc import sco_etud, sco_excel +import app.scodoc.sco_utils as scu + +from app import db +from sqlalchemy import text +from werkzeug.utils import secure_filename + + +@bp.route("/", methods=["GET"]) +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("/contacts", methods=["GET"]) +def contacts(): + contacts = ( + db.session.query(EntrepriseContact, Entreprise) + .join(Entreprise, EntrepriseContact.entreprise_id == Entreprise.id) + .all() + ) + logs = EntrepriseLog.query.order_by(EntrepriseLog.date.desc()).limit(LOGS_LEN).all() + return render_template( + "entreprises/contacts.html", title=("Contacts"), contacts=contacts, logs=logs + ) + + +@bp.route("/fiche_entreprise/", methods=["GET"]) +def fiche_entreprise(id): + entreprise = Entreprise.query.filter_by(id=id).first_or_404() + offres = entreprise.offres + offres_with_files = [] + for offre in offres: + files = [] + path = os.path.join( + Config.SCODOC_VAR_DIR, + "entreprises", + f"{offre.entreprise_id}", + f"{offre.id}", + ) + if os.path.exists(path): + for dir in glob.glob( + f"{path}/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]" + ): + for file in glob.glob(f"{dir}/*"): + file = [os.path.basename(dir), os.path.basename(file)] + files.append(file) + offres_with_files.append([offre, files]) + contacts = entreprise.contacts + logs = ( + EntrepriseLog.query.order_by(EntrepriseLog.date.desc()) + .filter_by(object=id) + .limit(LOGS_LEN) + .all() + ) + historique = ( + db.session.query(EntrepriseEtudiant, Identite) + .order_by(EntrepriseEtudiant.date_debut.desc()) + .filter(EntrepriseEtudiant.entreprise_id == id) + .join(Identite, Identite.id == EntrepriseEtudiant.etudid) + .all() + ) + return render_template( + "entreprises/fiche_entreprise.html", + title=("Fiche entreprise"), + entreprise=entreprise, + contacts=contacts, + offres=offres_with_files, + logs=logs, + historique=historique, + ) + + +@bp.route("/offres", methods=["GET"]) +def offres(): + offres_recus = ( + db.session.query(EntrepriseEnvoiOffre, EntrepriseOffre) + .filter(EntrepriseEnvoiOffre.receiver_id == current_user.id) + .join(EntrepriseOffre, EntrepriseOffre.id == EntrepriseEnvoiOffre.offre_id) + .all() + ) + return render_template( + "entreprises/offres.html", title=("Offres"), offres_recus=offres_recus + ) + + +@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(), + poste=form.poste.data.strip(), + service=form.service.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/ajout_entreprise.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("/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(offre) + db.session.add(log) + 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(hidden_entreprise_id=entreprise.id) + 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(), + poste=form.poste.data.strip(), + service=form.service.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() + contact.poste = form.poste.data.strip() + contact.service = form.service.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 + form.poste.data = contact.poste + form.service.data = contact.service + 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 = EntrepriseEtudiant( + 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/ajout_historique.html", title=("Ajout historique"), form=form + ) + + +@bp.route("/envoyer_offre/", methods=["GET", "POST"]) +def envoyer_offre(id): + offre = EntrepriseOffre.query.filter_by(id=id).first_or_404() + form = EnvoiOffreForm() + if form.validate_on_submit(): + responsable_data = form.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() + ) + envoi_offre = EntrepriseEnvoiOffre( + sender_id=current_user.id, receiver_id=responsable.id, offre_id=offre.id + ) + db.session.add(envoi_offre) + db.session.commit() + flash(f"L'offre a été envoyé à {responsable.get_nomplogin()}.") + return redirect(url_for("entreprises.fiche_entreprise", id=offre.entreprise_id)) + return render_template( + "entreprises/envoi_offre_form.html", title=("Envoyer une offre"), 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("/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) + + +@bp.route("/export_entreprises") +def export_entreprises(): + entreprises = Entreprise.query.all() + if entreprises: + keys = ["siret", "nom", "adresse", "ville", "codepostal", "pays"] + titles = keys[:] + L = [ + [entreprise.to_dict().get(k, "") for k in keys] + for entreprise in entreprises + ] + title = "entreprises" + xlsx = sco_excel.excel_simple_table(titles=titles, lines=L, sheet_name=title) + filename = title + return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE) + else: + abort(404) + + +@bp.route("/export_contacts") +def export_contacts(): + contacts = EntrepriseContact.query.all() + if contacts: + keys = ["nom", "prenom", "telephone", "mail", "poste", "service"] + titles = keys[:] + L = [[contact.to_dict().get(k, "") for k in keys] for contact in contacts] + title = "contacts" + xlsx = sco_excel.excel_simple_table(titles=titles, lines=L, sheet_name=title) + filename = title + return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE) + else: + abort(404) + + +@bp.route( + "/get_offre_file////" +) +def get_offre_file(entreprise_id, offre_id, filedir, filename): + if os.path.isfile( + os.path.join( + Config.SCODOC_VAR_DIR, + "entreprises", + f"{entreprise_id}", + f"{offre_id}", + f"{filedir}", + f"{filename}", + ) + ): + return send_file( + os.path.join( + Config.SCODOC_VAR_DIR, + "entreprises", + f"{entreprise_id}", + f"{offre_id}", + f"{filedir}", + f"{filename}", + ), + as_attachment=True, + ) + else: + abort(404) + + +@bp.route("/add_offre_file/", methods=["GET", "POST"]) +def add_offre_file(offre_id): + offre = EntrepriseOffre.query.filter_by(id=offre_id).first_or_404() + form = AjoutFichierForm() + if form.validate_on_submit(): + date = f"{datetime.now().strftime('%Y-%m-%d-%H-%M-%S')}" + path = os.path.join( + Config.SCODOC_VAR_DIR, + "entreprises", + f"{offre.entreprise_id}", + f"{offre.id}", + f"{date}", + ) + os.makedirs(path) + file = form.fichier.data + filename = secure_filename(file.filename) + file.save(os.path.join(path, filename)) + flash("Le fichier a été ajouté a l'offre.") + return redirect(url_for("entreprises.fiche_entreprise", id=offre.entreprise_id)) + return render_template( + "entreprises/form.html", title=("Ajout fichier à une offre"), form=form + ) + + +@bp.route("/delete_offre_file//", methods=["GET", "POST"]) +def delete_offre_file(offre_id, filedir): + offre = EntrepriseOffre.query.filter_by(id=offre_id).first_or_404() + form = SuppressionConfirmationForm() + if form.validate_on_submit(): + path = os.path.join( + Config.SCODOC_VAR_DIR, + "entreprises", + f"{offre.entreprise_id}", + f"{offre_id}", + f"{filedir}", + ) + if os.path.isdir(path): + shutil.rmtree(path) + flash("Le fichier relié à l'offre a été supprimé.") + return redirect( + url_for("entreprises.fiche_entreprise", id=offre.entreprise_id) + ) + return render_template( + "entreprises/delete_confirmation.html", + title=("Suppression fichier d'une offre"), + form=form, + ) diff --git a/app/models/__init__.py b/app/models/__init__.py index 0fee7bc48..642e31873 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 0734e35b0..7ed2f4b56 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 bdb5672a8..000000000 --- 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 220bf28be..eb018e704 100644 --- a/app/models/etudiants.py +++ b/app/models/etudiants.py @@ -144,6 +144,17 @@ class Identite(db.Model): ] return r[0] if r else None + def inscription_courante_date(self, date_debut, date_fin): + """La première inscription à un formsemestre incluant la + période [date_debut, date_fin] + """ + r = [ + ins + for ins in self.formsemestre_inscriptions + if ins.formsemestre.contient_periode(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 98edbba1e..4ada23e87 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -150,6 +150,13 @@ class FormSemestre(db.Model): today = datetime.date.today() return (self.date_debut <= today) and (today <= self.date_fin) + def contient_periode(self, date_debut, date_fin) -> bool: + """Vrai si l'intervalle [date_debut, date_fin] est + inclus 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 000000000..0eb8ebfee --- /dev/null +++ b/app/templates/entreprises/_contact.html @@ -0,0 +1,14 @@ +{# -*- mode: jinja-html -*- #} +
+

+ 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 000000000..ea69110e9 --- /dev/null +++ b/app/templates/entreprises/_offre.html @@ -0,0 +1,21 @@ +{# -*- mode: jinja-html -*- #} +
+

+ Intitulé : {{ offre[0].intitule }}
+ Description : {{ offre[0].description }}
+ Type de l'offre : {{ offre[0].type_offre }}
+ Missions : {{ offre[0].missions }}
+ Durée : {{ offre[0].duree }}
+ {% for fichier in offre[1] %} + {{ fichier[1] }} + supprimer
+ {% endfor %} + Ajoutez un fichier +

+ + +
\ No newline at end of file diff --git a/app/templates/entreprises/ajout_entreprise.html b/app/templates/entreprises/ajout_entreprise.html new file mode 100644 index 000000000..15cf174fb --- /dev/null +++ b/app/templates/entreprises/ajout_entreprise.html @@ -0,0 +1,54 @@ +{# -*- mode: jinja-html -*- #} +{% extends 'base.html' %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block app_content %} +

{{ title }}

+
+
+
+

+ Les champs s'autocomplète selon le SIRET +

+ {{ wtf.quick_form(form, novalidate=True) }} +
+
+ +{% endblock %} \ No newline at end of file diff --git a/app/templates/entreprises/ajout_historique.html b/app/templates/entreprises/ajout_historique.html new file mode 100644 index 000000000..c0a546d59 --- /dev/null +++ b/app/templates/entreprises/ajout_historique.html @@ -0,0 +1,32 @@ +{# -*- mode: jinja-html -*- #} +{% 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/app/templates/entreprises/contacts.html b/app/templates/entreprises/contacts.html new file mode 100644 index 000000000..54d77bd55 --- /dev/null +++ b/app/templates/entreprises/contacts.html @@ -0,0 +1,47 @@ +{# -*- mode: jinja-html -*- #} +{% 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 contacts

+ {% if contacts %} +
+ + + + + + + + + {% for contact in contacts %} + + + + + + + + {% endfor %} +
NomPrenomTelephoneMailEntreprise
{{ contact[0].nom }}{{ contact[0].prenom }}{{ contact[0].telephone }}{{ contact[0].mail }}{{ contact[1].nom }}
+ {% else %} +
Aucun contact présent dans la base
+
+ {% endif %} +
+ {% if contacts %} + Exporter la liste des contacts + {% endif %} +
+
+{% endblock %} \ 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 000000000..4894bca3e --- /dev/null +++ b/app/templates/entreprises/delete_confirmation.html @@ -0,0 +1,15 @@ +{# -*- mode: jinja-html -*- #} +{% 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 000000000..fe790a8d6 --- /dev/null +++ b/app/templates/entreprises/entreprises.html @@ -0,0 +1,63 @@ +{# -*- mode: jinja-html -*- #} +{% 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 + {% if entreprises %} + Exporter la liste des entreprises + {% endif %} +
+
+{% endblock %} \ No newline at end of file diff --git a/app/templates/entreprises/envoi_offre_form.html b/app/templates/entreprises/envoi_offre_form.html new file mode 100644 index 000000000..f67cb4e40 --- /dev/null +++ b/app/templates/entreprises/envoi_offre_form.html @@ -0,0 +1,32 @@ +{# -*- mode: jinja-html -*- #} +{% 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/app/templates/entreprises/fiche_entreprise.html b/app/templates/entreprises/fiche_entreprise.html new file mode 100644 index 000000000..d34531207 --- /dev/null +++ b/app/templates/entreprises/fiche_entreprise.html @@ -0,0 +1,76 @@ +{# -*- mode: jinja-html -*- #} +{% 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 }} en + {{ data[0].formation_text }} + +
  • + {% 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[0].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 000000000..066224d8b --- /dev/null +++ b/app/templates/entreprises/form.html @@ -0,0 +1,19 @@ +{# -*- mode: jinja-html -*- #} +{% 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/app/templates/entreprises/offres.html b/app/templates/entreprises/offres.html new file mode 100644 index 000000000..ff3ab9bdc --- /dev/null +++ b/app/templates/entreprises/offres.html @@ -0,0 +1,29 @@ +{# -*- mode: jinja-html -*- #} +{% extends 'base.html' %} + +{% block app_content %} +
+

{{ title }}

+ {% if offres_recus %} +
+
+ {% for offre in offres_recus %} +
+

+ Date envoi : {{ offre[0].date_envoi.strftime('%d/%m/%Y %H:%M') }}
+ Intitulé : {{ offre[1].intitule }}
+ Description : {{ offre[1].description }}
+ Type de l'offre : {{ offre[1].type_offre }}
+ Missions : {{ offre[1].missions }}
+ Durée : {{ offre[1].duree }}
+

+
+ {% endfor %} +
+
+ {% else %} +
Aucune offre reçue
+
+ {% endif %} +
+{% endblock %} \ No newline at end of file diff --git a/migrations/versions/2dfafee725ae_creation_table_relations_entreprrises.py b/migrations/versions/2dfafee725ae_creation_table_relations_entreprrises.py new file mode 100644 index 000000000..05adc3566 --- /dev/null +++ b/migrations/versions/2dfafee725ae_creation_table_relations_entreprrises.py @@ -0,0 +1,255 @@ +"""creation tables relations entreprises + +Revision ID: f3b62d64efa3 +Revises: 91be8a06d423 +Create Date: 2021-12-24 10:36:27.150085 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = "f3b62d64efa3" +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_etudiant", + 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"], ondelete="cascade" + ), + 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"], ondelete="cascade" + ), + sa.PrimaryKeyConstraint("id"), + ) + + op.create_table( + "entreprise_envoi_offre", + sa.Column("id", sa.Integer(), nullable=False), + sa.Column("sender_id", sa.Integer(), nullable=True), + sa.Column("receiver_id", sa.Integer(), nullable=True), + sa.Column("offre_id", sa.Integer(), nullable=True), + sa.Column( + "date_envoi", + sa.DateTime(timezone=True), + server_default=sa.text("now()"), + nullable=True, + ), + sa.ForeignKeyConstraint( + ["offre_id"], + ["entreprise_offre.id"], + ), + sa.ForeignKeyConstraint( + ["sender_id"], + ["user.id"], + ), + sa.ForeignKeyConstraint( + ["receiver_id"], + ["user.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.add_column("entreprise_contact", sa.Column("poste", sa.Text(), nullable=True)) + op.add_column("entreprise_contact", sa.Column("service", sa.Text(), nullable=True)) + op.drop_column("entreprise_contact", "description") + op.drop_column("entreprise_contact", "enseignant") + op.drop_column("entreprise_contact", "date") + op.drop_column("entreprise_contact", "type_contact") + op.drop_column("entreprise_contact", "etudid") + op.drop_column("entreprise_contact", "entreprise_corresp_id") + + 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", "qualite_relation") + op.drop_column("entreprises", "note") + op.drop_column("entreprises", "contact_origine") + op.drop_column("entreprises", "plus10salaries") + op.drop_column("entreprises", "privee") + op.drop_column("entreprises", "secteur") + op.drop_column("entreprises", "date_creation") + op.drop_column("entreprises", "dept_id") + op.drop_column("entreprises", "localisation") + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column( + "entreprises", + sa.Column("localisation", 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( + "date_creation", + postgresql.TIMESTAMP(timezone=True), + server_default=sa.text("now()"), + autoincrement=False, + nullable=True, + ), + ) + op.add_column( + "entreprises", + sa.Column("secteur", sa.TEXT(), autoincrement=False, nullable=True), + ) + op.add_column( + "entreprises", + sa.Column("privee", sa.TEXT(), autoincrement=False, nullable=True), + ) + op.add_column( + "entreprises", + sa.Column("plus10salaries", sa.BOOLEAN(), autoincrement=False, nullable=True), + ) + op.add_column( + "entreprises", + sa.Column("contact_origine", sa.TEXT(), autoincrement=False, nullable=True), + ) + op.add_column( + "entreprises", sa.Column("note", sa.TEXT(), autoincrement=False, nullable=True) + ) + op.add_column( + "entreprises", + sa.Column("qualite_relation", sa.INTEGER(), 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( + "entreprise_corresp_id", sa.INTEGER(), autoincrement=False, nullable=True + ), + ) + op.add_column( + "entreprise_contact", + sa.Column("etudid", sa.INTEGER(), 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( + "date", + postgresql.TIMESTAMP(timezone=True), + 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("description", sa.TEXT(), 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", "service") + op.drop_column("entreprise_contact", "poste") + 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_envoi_offre") + op.drop_table("entreprise_offre") + op.drop_table("entreprise_etudiant") + op.drop_table("entreprise_log") + # ### end Alembic commands ### diff --git a/scodoc.py b/scodoc.py index 94c8a7f09..1be505527 100755 --- a/scodoc.py +++ b/scodoc.py @@ -248,6 +248,16 @@ def edit_role(rolename, addpermissionname=None, removepermissionname=None): # e db.session.add(role) db.session.commit() +@app.cli.command() +@click.argument("rolename") +def delete_role(rolename): + """Delete a role""" + role = Role.query.filter_by(name=rolename).first() + if role is None: + sys.stderr.write(f"delete_role: role {rolename} does not exists\n") + return 1 + db.session.delete(role) + db.session.commit() @app.cli.command() @click.argument("username")