diff --git a/app/db_mgr.py b/app/db_mgr.py index 401b546..787e9c2 100644 --- a/app/db_mgr.py +++ b/app/db_mgr.py @@ -1,4 +1,5 @@ from app.models import db, Period, Project, Category, ProjectStatus +from flask import flash # TODO: make this configurable, and choose another place to use it, # in 'routes.py' maybe @@ -108,15 +109,19 @@ def projects(): # we build the labels.name list of the intersection of current category's labels with project's labels # Comprehensive shortcut is: # labels = [_cl.label.name for _cl in category.labels if _cl.label in [_pl.label for _pl in project.labels]] - # + category_labels = [_cl.label for _cl in category.category_labels] project_labels = [_pl.label for _pl in project.project_labels] intersection_labels = list(set.intersection(set(project_labels), set(category_labels))) labels = [label.name for label in intersection_labels] project_row.append(labels) + + if not isinstance(project.name, type(None)): + all_projects.append(project_row) + # then add total charge project_row.append(_pc[1]) - all_projects.append(project_row) + return all_projects @@ -402,7 +407,8 @@ def charges_by_agent_tabled(agent_id): p2.name as project_name, s.name as service_name, c2.name as capacity_name, - c.charge_rate as charge_rate + c.charge_rate as charge_rate, + c.id as charge_id from charge as c join period p on c.period_id = p.id join project p2 on c.project_id = p2.id @@ -440,6 +446,15 @@ def charges_by_agent(agent_id): all_charges.append([p, 0]) return all_charges +def complete_charges_by_agent(agent_id): + sql_cmd = f"SELECT * from charge WHERE agent_id = {agent_id}" + request = db.session.execute(sql_cmd) + all_charges = [] + for item in request.fetchall(): + all_charges.append(item) + return all_charges + + def count_agents_by_status(period_id): sql_txt = f""" diff --git a/app/main/routes.py b/app/main/routes.py index b89fb3f..44fd1f4 100644 --- a/app/main/routes.py +++ b/app/main/routes.py @@ -1,11 +1,9 @@ - import xlsxwriter import io from flask import render_template, make_response, current_app, redirect, url_for, request, flash, abort, send_file, \ jsonify from flask_login import login_required, current_user - from . import bp from app.models import Agent, Project, Service, Capacity, Period, db, Company, AgentGrade, AgentStatus, AgentBap, \ @@ -16,7 +14,6 @@ from app.auth.routes import role_required updated_message = "mis à jour" added_message = "ajouté" - def form_manager(object_class, object_id_key, object_id, @@ -24,6 +21,7 @@ def form_manager(object_class, form_struct, urlfor_exist_dict, urlfor_done_dict): + """ Generic form manager, holds etheir POST or GET method. Allows to modify existing object, or create new one. @@ -39,13 +37,25 @@ def form_manager(object_class, :param urlfor_done_dict: Where do redirect when done. :return: """ + if request.method == 'GET': + if object_id: this_object = object_class.query.get(int(object_id)) else: this_object = object_class() + object_struct = this_object.to_struct() - return render_template(form_template, object_struct=object_struct, + name_object = object_struct['name'] + + if type(name_object) != str: + flash(f">{name_object}< .", 'warning') + else: + #flash(f"> attention pas d'informations renseignées < .", 'warning') + pass + + return render_template(form_template, + object_struct=object_struct, **form_struct) elif request.method == 'POST': try: @@ -58,9 +68,10 @@ def form_manager(object_class, done_string = updated_message + "." else: # check name doesnt exist yet - existing_object = object_class.query.filter(object_class.name == request.form.get('name')).one_or_none() + existing_object = object_class.query.filter( + object_class.name == request.form.get('name')).one_or_none() if existing_object: - flash(f">{existing_object.name}< existe déjà.", 'warning') + flash(f">le projet {existing_object.name}< existe déjà.", 'warning') return redirect(url_for(**urlfor_exist_dict)) # or create from scratch this_object = object_class() @@ -72,8 +83,64 @@ def form_manager(object_class, # we're done flash(f"{this_object.name} (#{this_object.id}) " + done_string) urlfor_done_dict[object_id_key] = this_object.id + return redirect(url_for(**urlfor_done_dict)) +def form_delete_project(object_class, + form_template, + object_id_key, + object_id, + form_struct, + form_template_two): + + list_project = form_struct['projects'] + List_project = [] + + if request.method == 'GET': + if object_id: + this_object = object_class.query.get(int(object_id)) + else: + this_object = object_class() + object_struct = this_object.to_struct() + + for j in enumerate(list_project): + List_project.append(j[1][1]) + + return render_template(form_template, + object_struct=object_struct, + **form_struct) + + elif request.method == 'POST': + + name_project = request.form.get('name') + + for j in enumerate(list_project): + List_project.append(j[1][1]) + + if name_project in List_project : + + existing_object = object_class.query.filter(object_class.name == name_project).one_or_none() + + flash(f">projet {existing_object.name}< supprimé") + + object_struct = existing_object.to_struct() + + db.session.delete(existing_object) + db.session.commit() + + return render_template(form_template_two, + object_struct=object_struct) + else: + + flash(f">projet {name_project}< déjà supprimé.", 'warning') + + this_object = object_class() + _object_struct = this_object.to_struct() + + return render_template(form_template, + object_struct=_object_struct, + **form_struct) + @bp.before_request def site_login(): @@ -85,24 +152,20 @@ def site_login(): # no such config, juste ignore pass - @bp.before_request def catch_all_route(): current_app.logger.debug(f"{request.method} {request.path}") - @bp.route('/') def index(): return render_template('index.html', subtitle="Page d'accueil") - @bp.route('/total_charge') @role_required('service') def total_charge(): return render_template('total_charge.html', subtitle="Charge des agents", total_charges=db_mgr.charges_for_all_agents()) - @bp.route('/services') @role_required('service') def services(): @@ -113,7 +176,6 @@ def services(): return render_template('services.html', subtitle="Liste des services ({})".format(num_services), services=all_services) - @bp.route('/service/') @role_required('service') def service(service_id): @@ -121,7 +183,6 @@ def service(service_id): return render_template('service.html', service=this_service.to_struct(), subtitle=f"{this_service.name}") - @bp.route('/service/create', methods=('POST', 'GET')) @bp.route('/service//edit', methods=('POST', 'GET')) @role_required('service') @@ -135,7 +196,6 @@ def service_edit(service_id=None): urlfor_exist_dict={'endpoint': 'main.service_edit'}, urlfor_done_dict={'endpoint': 'main.service', 'service_id': None}) - @bp.route('/service//delete', methods=('POST', 'GET')) @role_required('admin') def service_delete(service_id=None): @@ -144,7 +204,6 @@ def service_delete(service_id=None): # flash(f" {this_service.name} effacé") return redirect(url_for('main.service', service_id=this_service.id)) - @bp.route('/projects') @role_required('project') def projects(): @@ -155,7 +214,6 @@ def projects(): return render_template('projects.html', subtitle="Liste des projets ({})".format(num_projects), projects=all_projects) - @bp.route('/project/') @role_required('project') def project(project_id): @@ -167,7 +225,6 @@ def project(project_id): charges=charges_table, subtitle="{}".format(this_project.name)) - @bp.route('/project/create', methods=('POST', 'GET')) @bp.route('/project//edit', methods=('POST', 'GET')) @role_required('service') @@ -182,15 +239,20 @@ def project_edit(project_id=None): urlfor_exist_dict={'endpoint': 'main.project_edit'}, urlfor_done_dict={'endpoint': 'main.project', 'project_id': None}) - -@bp.route('/project//delete', methods=('POST', 'GET')) +@bp.route('/project/delete', methods=('POST', 'GET')) +@bp.route('/project//delete', methods=('POST', 'GET')) @role_required('admin') def project_delete(project_id=None): - this_project = Project.query.get(int(project_id)) - flash("Suppression du Projet pas encore implémentée", 'warning') - # flash(f" {this_project.name} effacé") - return redirect(url_for('main.project', project_id=this_project.id)) + _form_struct = {'projects': db_mgr.projects()} + _form_template_delete='project_delete_form.html', + _form_template_res='project_delete_res.html', + return form_delete_project(object_class=Project, + object_id_key='project_id', + object_id=project_id, + form_template=_form_template_delete, + form_struct=_form_struct, + form_template_two=_form_template_res) @bp.route('/projects/stats') @role_required('project') @@ -200,18 +262,16 @@ def projects_stats(): return render_template('projects_stats.html', subtitle="Statistiques des projets ({})".format(num_projects), categories=this_categories) - @bp.route('/agents') @role_required('project') def agents(): # get agents list - all_agents = db_mgr.agents() + all_agents = db_mgr.agents() num_agents = len(all_agents) # pass to template return render_template('agents.html', subtitle="Liste des agents ({})".format(num_agents), agents=all_agents) - @bp.route('/agent/') @role_required('agent') def agent(agent_id): @@ -223,7 +283,6 @@ def agent(agent_id): charges=charges_table, subtitle=f"{this_agent.fullname}") - @bp.route('/agent/create', methods=('POST', 'GET')) @bp.route('/agent//edit', methods=('POST', 'GET')) @role_required('service') @@ -240,7 +299,6 @@ def agent_edit(agent_id=None): urlfor_exist_dict={'endpoint': 'main.agent_edit'}, urlfor_done_dict={'endpoint': 'main.agent', 'agent_id': None}) - @bp.route('/agent//delete', methods=('POST', 'GET')) @role_required('admin') def agent_delete(agent_id=None): @@ -249,7 +307,6 @@ def agent_delete(agent_id=None): # flash(f"Agent {this_agent.name} effacé") return redirect(url_for('main.agent', agent_id=this_agent.id)) - @bp.route('/agents/stats') @role_required('project') def agents_stats(): @@ -258,16 +315,15 @@ def agents_stats(): return render_template('agents_stats.html', subtitle="Statistiques des agents ({})".format(num_agents), periods=all_periods) - @bp.route('/agents/responsabilities') @role_required('agent') def responsabilities(): num_resp = len(AgentResponsability.query.all()) - all_resp = AgentResponsability.query.order_by(AgentResponsability.name).all() + all_resp = AgentResponsability.query.order_by( + AgentResponsability.name).all() return render_template('responsabilities.html', subtitle="Liste des Responsabilités ({})".format(num_resp), responsabilities=all_resp) - @bp.route('/responsability/') @role_required('agent') def responsability(responsability_id): @@ -275,7 +331,6 @@ def responsability(responsability_id): return render_template('responsability.html', subtitle=f"{this_responsability.name}", responsability=this_responsability) - @bp.route('/responsability/create', methods=('POST', 'GET')) @bp.route('/responsability//edit', methods=('POST', 'GET')) @role_required('admin') @@ -285,10 +340,10 @@ def responsability_edit(responsability_id=None): object_id=responsability_id, form_template='responsability_form.html', form_struct={}, - urlfor_exist_dict={'endpoint': 'main.responsability_edit'}, + urlfor_exist_dict={ + 'endpoint': 'main.responsability_edit'}, urlfor_done_dict={'endpoint': 'main.responsability', 'responsability_id': None}) - @bp.route('/responsability//delete', methods=('POST', 'GET')) @role_required('admin') def responsability_delete(responsability_id=None): @@ -297,7 +352,6 @@ def responsability_delete(responsability_id=None): # flash(f"Statut de Projet {this_status.name} effacé") return redirect(url_for('main.responsability', responsability_id=this_responsability.id)) - @bp.route('/employments') @login_required def employments(): @@ -308,7 +362,6 @@ def employments(): return render_template('employments.html', subtitle="Liste des Emplois Types ({})".format(num_employments), employments=all_employments) - @bp.route('/skills') @login_required def skills(): @@ -319,7 +372,6 @@ def skills(): return render_template('skills.html', subtitle="Liste des compétences ({})".format(num_skills), skills=all_skills) - @bp.route('/skill/') @role_required('agent') def skill(skill_id): @@ -327,7 +379,6 @@ def skill(skill_id): return render_template('skill.html', subtitle=f"{this_skill.name}", skill=this_skill) - @bp.route('/skill/create', methods=('POST', 'GET')) @bp.route('/skill//edit', methods=('POST', 'GET')) @role_required('admin') @@ -340,7 +391,6 @@ def skill_edit(skill_id=None): urlfor_exist_dict={'endpoint': 'main.skill_edit'}, urlfor_done_dict={'endpoint': 'main.skill', 'skill_id': None}) - @bp.route('/skill//delete', methods=('POST', 'GET')) @role_required('admin') def skill_delete(skill_id=None): @@ -348,18 +398,17 @@ def skill_delete(skill_id=None): flash("Suppression de la Compétence pas encore implémentée", 'warning') return redirect(url_for('main.skill', skill_id=this_skill.id)) - @bp.route('/project_statuses') @login_required def project_statuses(): # get project_statuses list - all_project_statuses = ProjectStatus.query.order_by(ProjectStatus.name).all() + all_project_statuses = ProjectStatus.query.order_by( + ProjectStatus.name).all() num_project_statuses = len(all_project_statuses) # pass to template return render_template('project_statuses.html', subtitle="Statuts de projets ({})".format(num_project_statuses), project_statuses=all_project_statuses) - @bp.route('/project_status/') @role_required('admin') def project_status(status_id): @@ -369,7 +418,6 @@ def project_status(status_id): return render_template('project_status.html', subtitle=f"{this_status.name}", status=this_status) - @bp.route('/project_status/create', methods=('POST', 'GET')) @bp.route('/project_status//edit', methods=('POST', 'GET')) @role_required('admin') @@ -379,10 +427,10 @@ def project_status_edit(status_id=None): object_id=status_id, form_template='project_status_form.html', form_struct={}, - urlfor_exist_dict={'endpoint': 'main.project_status_edit'}, + urlfor_exist_dict={ + 'endpoint': 'main.project_status_edit'}, urlfor_done_dict={'endpoint': 'main.project_status', 'status_id': None}) - @bp.route('/project_status//delete', methods=('POST', 'GET')) @role_required('admin') def project_status_delete(status_id=None): @@ -391,7 +439,6 @@ def project_status_delete(status_id=None): # flash(f"Statut de Projet {this_status.name} effacé") return redirect(url_for('main.project_status', status_id=this_status.id)) - @bp.route('/capacities') @login_required def capacities(): @@ -402,7 +449,6 @@ def capacities(): return render_template('capacities.html', subtitle="Liste des fonctions ({})".format(num_capacities), capacities=all_capacities) - @bp.route('/capacity/') @role_required('admin') def capacity(capacity_id): @@ -412,7 +458,6 @@ def capacity(capacity_id): return render_template('capacity.html', subtitle=f"{this_capacity.name}", capacity=this_capacity) - @bp.route('/capacity/create', methods=('POST', 'GET')) @bp.route('/capacity//edit', methods=('POST', 'GET')) @role_required('admin') @@ -425,7 +470,6 @@ def capacity_edit(capacity_id=None): urlfor_exist_dict={'endpoint': 'main.capacity_edit'}, urlfor_done_dict={'endpoint': 'main.capacity', 'capacity_id': None}) - @bp.route('/capacity//delete', methods=('POST', 'GET')) @role_required('admin') def capacity_delete(capacity_id=None): @@ -434,7 +478,6 @@ def capacity_delete(capacity_id=None): # flash(f"Label {this_label.name} effacé") return redirect(url_for('main.capacities')) - @bp.route('/labels') @login_required def labels(): @@ -445,7 +488,6 @@ def labels(): return render_template('labels.html', subtitle="Liste des labels ({})".format(num_labels), labels=all_labels) - @bp.route('/label/') @role_required('admin') def label(label_id): @@ -455,7 +497,6 @@ def label(label_id): return render_template('label.html', subtitle=f"{this_label.name}", label=this_label) - @bp.route('/label/create', methods=('POST', 'GET')) @bp.route('/label//edit', methods=('POST', 'GET')) @role_required('admin') @@ -469,7 +510,6 @@ def label_edit(label_id=None): urlfor_exist_dict={'endpoint': 'main.label_edit'}, urlfor_done_dict={'endpoint': 'main.label', 'label_id': None}) - @bp.route('/label//delete', methods=('POST', 'GET')) @role_required('admin') def label_delete(label_id=None): @@ -478,7 +518,6 @@ def label_delete(label_id=None): # flash(f"Label {this_label.name} effacé") return redirect(url_for('main.labels')) - @bp.route('/categories') @login_required def categories(): @@ -489,7 +528,6 @@ def categories(): return render_template('categories.html', subtitle="Liste des categories ({})".format(num_categories), categories=all_categories) - @bp.route('/category/') @role_required('admin') def category(category_id): @@ -499,7 +537,6 @@ def category(category_id): return render_template('category.html', subtitle=f"{this_category.name}", category=this_category) - @bp.route('/category/create', methods=('POST', 'GET')) @bp.route('/category//edit', methods=('POST', 'GET')) @role_required('admin') @@ -513,7 +550,6 @@ def category_edit(category_id=None): urlfor_exist_dict={'endpoint': 'main.category_edit'}, urlfor_done_dict={'endpoint': 'main.category', 'category_id': None}) - @bp.route('/category//delete', methods=('POST', 'GET')) @role_required('admin') def category_delete(category_id=None): @@ -522,7 +558,6 @@ def category_delete(category_id=None): # flash(f"Category {this_category.name} effacé") return redirect(url_for('main.categories')) - @bp.route('/periods') @login_required def periods(): @@ -533,7 +568,6 @@ def periods(): return render_template('periods.html', subtitle="Liste des périodes ({})".format(num_periods), periods=all_periods) - @bp.route('/charge/add', methods=('POST', 'GET')) @role_required('service') def charge_add(): @@ -568,7 +602,6 @@ def charge_add(): flash(f"Nouvelle charge pour l'agent {this_agent.fullname}") return redirect(url_for('main.agent', agent_id=this_agent.id)) - # - - - - - - - - - - - - - - - - - - - - REST API - - - - - - - - - - - - - - - - - - - - def array_to_any(array, format='csv'): @@ -624,7 +657,8 @@ def array_to_jsonhs(categorised_array): project_charges = [] for charge_line in categorised_array[1:-1]: period = charge_line[0] - project_charge = charge_line[project_index] + if (project_index < len(charge_line)): + project_charge = charge_line[project_index] project_charges.append([period, float(project_charge)]) # add project_charges only if non zero total_project_charge = 0 @@ -671,7 +705,6 @@ def array_to_xls(array): return file_to_return - def array_to_csv(array, sep=','): csv_table = [] for line in array: @@ -682,37 +715,123 @@ def array_to_csv(array, sep=','): resp.headers['Content-Type'] = 'text/plain;charset=utf8' return resp - @bp.route('/charge/project///') @bp.route('/charge/project//') @role_required('project') def rest_charge_project(project_id, sort_key, any_format='csv'): return array_to_any(db_mgr.charges_by_project_stacked(project_id, sort_key), any_format) - @bp.route('/charge/agent//') @bp.route('/charge/agent/') @role_required('service') def rest_charge_agent(agent_id, any_format='csv'): return array_to_any(db_mgr.charges_by_agent_stacked(agent_id), any_format) +@bp.route('/charge/edit/') +def edit_charge_page(charge_id): + this_charge = Charge.query.get(charge_id) -@bp.route('/charge/labels//') -@bp.route('/charge/labels/') -@role_required('project') + if (this_charge): + this_agents = Agent.query.order_by(Agent.firstname).all() + this_projects = Project.query.order_by(Project.name).all() + this_services = Service.query.order_by(Service.name).all() + this_periods = Period.query.order_by(Period.id).all() + this_capacities = Capacity.query.order_by(Capacity.name).all() + + # Get current params for this charge + current_agent = None + current_project = None + current_service = None + current_period = None + current_capacity = None + for a in this_agents: + if (a.id == this_charge.agent_id): + current_agent = a + break + + for p in this_projects: + if (p.id == this_charge.project_id): + current_project = p + break + + for s in this_services: + if (s.id == this_charge.service_id): + current_service = s + break + + for p in this_periods: + if (p.id == this_charge.period_id): + current_period = p + break + + for c in this_capacities: + if (c.id == this_charge.capacity_id): + current_capacity = c + break + + return render_template('edit_charge.html', subtitle="Modifier une charge", + agents=this_agents, + projects=this_projects, + services=this_services, + periods=this_periods, + capacities=this_capacities, + charge=this_charge, + cur_agent=current_agent, + cur_project=current_project, + cur_service=current_service, + cur_period=current_period, + cur_capacity=current_capacity) + else: + flash(f"Charge avec l'id {charge_id} n'existe pas") + return redirect(url_for('main.agents')) + + +@bp.route('/charge/update', methods=['PUT', 'POST']) +def update_charge_values(): + # TODO: set role required + this_charge = Charge.query.get(request.form.get('charge_id')) + if (this_charge == None): + return 'This charge doesn\'t exist. (charge_id unknown)', 400 + + this_charge.from_request(request) + db.session.commit() + flash(f"Charge {request.form.get('charge_id')} a été mis à jour") + return redirect(url_for('main.edit_charge_page', charge_id=request.form.get('charge_id'))) + # return 'Charge has been updated', 200 + + +@bp.route('/charge/delete', methods=['DELETE', 'POST']) +def remove_charge_values(): + # TODO: set role required + + this_charge = Charge.query.get(request.form.get('charge_id')) + if (this_charge == None): + return 'This charge doesn\'t exist. (charge_id unknown)', 400 + + db.session.delete(this_charge) + db.session.commit() + + flash(f"charge {request.form.get('charge_id')} supprimée") + # redirect on agent + return redirect(url_for('main.agent', agent_id=request.form.get('agent_id'))) + + +@ bp.route('/charge/labels//') +@ bp.route('/charge/labels/') +@ role_required('project') def rest_labels_stats(category_id, any_format='csv'): return array_to_any(db_mgr.charges_by_labels_stacked(category_id), any_format) -@bp.route('/charge/projects/') -@bp.route('/charge/projects') -@role_required('project') +@ bp.route('/charge/projects/') +@ bp.route('/charge/projects') +@ role_required('project') def rest_projects_stats(any_format='csv'): return array_to_any(db_mgr.charges_for_projects_stacked(), any_format) -@bp.route('/count/agents/by_status//') -@bp.route('/count/agents/by_status/') -@role_required('project') +@ bp.route('/count/agents/by_status//') +@ bp.route('/count/agents/by_status/') +@ role_required('project') def rest_agents_status_count(period_id, any_format='csv'): return array_to_any(db_mgr.count_agents_by_status(period_id), any_format) diff --git a/app/main/templates/agent.html b/app/main/templates/agent.html index 2726753..bc13274 100644 --- a/app/main/templates/agent.html +++ b/app/main/templates/agent.html @@ -1,155 +1,117 @@ {% extends "base_page.html" %} {% block more_heads %} - + {% endblock %} {% block content %} - - + + -
-
- Fiche Agent -
-
-
-
ID :
-
{{ agent.id }}
-
Genre :
-
-
Date de naissance :
-
-
E-mail :
-
- {# TODO: put different spacing #} -
-
-
Statut :
-
{{ agent.status.name }}
-
Organisme :
-
{{ agent.company.name }}
-
Corps :
-
-
Grade :
-
{{ agent.grade.name }}
-
BAP :
-
{{ agent.bap.name }}
- {# TODO: puth different spacing #} -
-
-
Quotité :
-
-
Date arrivée :
-
-
Date départ :
-
- {# TODO: put different spacing #} -
-
-
Commentaires :
-
- {# TODO: put different spacing #} -
-
-
Fiche de poste :
-
- {# TODO: put different spacing #} -
-
-
Compétences :
-
-
Besoin en formation :
-
-
- {{ commons.charge_link( url_for('main.charge_add', agent_id=agent.id), 25) }} - {{ commons.edit_link(url_for('main.agent_edit', agent_id=agent.id), 25) }} - {{ commons.delete_link( url_for('main.agent_delete', agent_id=agent.id), 25) }} -
+
+
+ Fiche Agent
+
+
+
ID :
+
{{ agent.id }}
+
Genre :
+
+
Date de naissance :
+
+
E-mail :
+
+ {# TODO: put different spacing #} +
+
+
Statut :
+
{{ agent.status.name }}
+
Organisme :
+
{{ agent.company.name }}
+
Corps :
+
+
Grade :
+
{{ agent.grade.name }}
+
BAP :
+
{{ agent.bap.name }}
+ {# TODO: puth different spacing #} +
+
+
Quotité :
+
+
Date arrivée :
+
+
Date départ :
+
+ {# TODO: put different spacing #} +
+
+
Commentaires :
+
+ {# TODO: put different spacing #} +
+
+
Fiche de poste :
+
+ {# TODO: put different spacing #} +
+
+
Compétences :
+
+
Besoin en formation :
+
+
+ {{ commons.charge_link( url_for('main.charge_add', agent_id=agent.id), 25) }} + {{ commons.edit_link(url_for('main.agent_edit', agent_id=agent.id), 25) }} + {{ commons.delete_link( url_for('main.agent_delete', agent_id=agent.id), 25) }} +
+
-
+
- - +
+ {% for header in charges[0] %} - + {% endfor %} + - - + + {% for line in charges[1:] %} - - {% for i in range(line|length) %} - {% if 'Charge' in charges[0][i] %} - {% set charge = line[i] | int %} - - {% else %} - - {% endif %} - {% endfor %} - + + {% for i in range(line|length - 1) %} + {% if 'Charge' in charges[0][i] %} + {% set charge = line[i] | int %} + + {% else %} + + {% endif %} + {% endfor %} + + {% endfor %} - -
{{ header }}{{ header }}Edit
{{ charge / 100 }}{{ line[i] }}
{{ charge / 100 }}{{ line[i] }} + {{ commons.edit_link(url_for('main.edit_charge_page', charge_id=line[-1])) }} +
+ + {% endblock %} {% block more_scripts %} - {% include 'hg-includes.html' %} -{# {% include 'd3js-includes.html' %}#} - {% include 'charges-includes.html' %} - - + -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/app/main/templates/agents_stats.html b/app/main/templates/agents_stats.html index 895107a..7e9987b 100644 --- a/app/main/templates/agents_stats.html +++ b/app/main/templates/agents_stats.html @@ -1,99 +1,126 @@ {% extends "base_page.html" %} {% block more_heads %} - + {% endblock %} {% block content %} - - - -
- - - -
+ + -

Tout le laboratoire

+
+ + + +
-
-
-
-
-
-
-
-
+

Tout le laboratoire

- {# {% for c in categories %}#} - {#

Charge pour la catégorie {{ c.name }}

#} - {#
#} - {# {% endfor %}#} +
+
+
+
+
+
+
+
{% endblock %} {% block more_scripts %} - {% include 'd3js-includes.html' %} - {% include 'charges-includes.html' %} - - -{% endblock %} + +{% endblock %} \ No newline at end of file diff --git a/app/main/templates/edit_charge.html b/app/main/templates/edit_charge.html new file mode 100644 index 0000000..cbcc6c6 --- /dev/null +++ b/app/main/templates/edit_charge.html @@ -0,0 +1,84 @@ +{% extends "base_page.html" %} + +{% block more_heads %} + +{% endblock %} + +{% block content %} + +{% if charge %} + +
+
+ + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +
+ +
+ +
+ + + +
+ + +{% else %} + +

Charge does not exist. (charge id not found)

+ +{% endif %} + +{% endblock %} \ No newline at end of file diff --git a/app/main/templates/project.html b/app/main/templates/project.html index 083292a..12cb3cd 100644 --- a/app/main/templates/project.html +++ b/app/main/templates/project.html @@ -1,79 +1,151 @@ {% extends "base_page.html" %} {% block more_heads %} - + {% endblock %} {% block content %} - - + + -
-
- Fiche Projet -
-
-
-
ID :
-
{{ project['id'] }}
- {% for category, labels in project['category_labels'].items() %} -
{{ category }} :
-
{{ labels|join(",") }}
- {% endfor %} -
Etat :
-
{{ project['status_name'] }}
-
Date RSP :
-
{{ project['rsp_date'] }}
-
Fiche projet :
-
-
Date MAJ de la fiche :
-
{{ project['update_date'] }}
-
Commentaire :
-
-
- {{ commons.charge_link( url_for('main.charge_add', project_id=project.id), 25) }} - {{ commons.edit_link(url_for('main.project_edit', project_id=project.id), 25) }} - {{ commons.delete_link( url_for('main.project_delete', project_id=project.id), 25) }} -
-
+
+
+ Fiche Projet +
+
+
+
ID :
+
{{ project['id'] }}
+ {% for category, labels in project['category_labels'].items() %} +
{{ category }} :
+
{{ labels|join(",") }}
+ {% endfor %} +
Etat :
+
{{ project['status_name'] }}
+
Date RSP :
+
{{ project['rsp_date'] }}
+
Fiche projet :
+
+
Date MAJ de la fiche :
+
{{ project['update_date'] }}
+
Commentaire :
+
+
+ {{ commons.charge_link( url_for('main.charge_add', project_id=project.id), 25) }} + {{ commons.edit_link(url_for('main.project_edit', project_id=project.id), 25) }} + {{ commons.delete_link( url_for('main.project_delete', project_id=project.id), 25) }} +
+
-
-
-
-
- - - - {% for header in charges[0] %} - - {% endfor %} - - - - {% for line in charges[1:] %} - - {% for cell in line %} - - {% endfor %} - - {% endfor %} - -
{{ header }}
{{ cell }}
+
+
+
+
+ + + + {% for header in charges[0] %} + + {% endfor %} + + + + {% for line in charges[1:] %} + + {% for cell in line %} + + {% endfor %} + + {% endfor %} + +
{{ header }}
{{ cell }}
{% endblock %} {% block more_scripts %} - {% include 'd3js-includes.html' %} - {% include 'charges-includes.html' %} +{% include 'hg-includes.html' %} +{% include 'charges-includes.html' %} - -{% endblock %} + +{% endblock %} \ No newline at end of file diff --git a/app/main/templates/project_delete_form.html b/app/main/templates/project_delete_form.html new file mode 100644 index 0000000..23f971b --- /dev/null +++ b/app/main/templates/project_delete_form.html @@ -0,0 +1,32 @@ +{% extends "base_page.html" %} + +{# Make link with form_manager #} +{% set project = object_struct %} + +{# Set the title that will be used in base_page #} + +{% if project['id'] and project['id'] != '' and project['name'] != None %} +{% set subtitle = "Supprimer le projet " + project['name'] %} +{% else %} +{% set subtitle = "Suppression pour nom de projet None" %} +{% endif %} + +{% block content %} + + + + +
+ {% if project['id'] and project['id'] != '' %} + + {% endif %} + +
+ + +
+ +
+{% endblock %} \ No newline at end of file diff --git a/app/main/templates/project_delete_res.html b/app/main/templates/project_delete_res.html new file mode 100644 index 0000000..540b462 --- /dev/null +++ b/app/main/templates/project_delete_res.html @@ -0,0 +1,24 @@ +{% extends "base_page.html" %} + +{# Make link with form_manager #} +{% set project = object_struct %} + +{# Set the title that will be used in base_page #} +{% if project['id'] and project['id'] != '' %} + {% set subtitle = "Suppresion du projet " + project['name'] %} +{% else %} + {% set subtitle = "Projet " + project['name'] + "supprimé" %} +{% endif %} + +{% block content %} + + + + + {% if project['id'] and project['id'] != '' %} + + {% endif %} + +
+ +{% endblock %} diff --git a/app/main/templates/project_form.html b/app/main/templates/project_form.html index a826c3b..bb69536 100644 --- a/app/main/templates/project_form.html +++ b/app/main/templates/project_form.html @@ -4,8 +4,9 @@ {% set project = object_struct %} {# Set the title that will be used in base_page #} -{% if project['id'] and project['id'] != '' %} - {% set subtitle = "Modifier le projet "+ project['name'] %} + +{% if project['id'] and project['id'] != '' and project['name'] != None %} + {% set subtitle = "Modifier le projet " + project['name'] %} {% else %} {% set subtitle = "Ajouter un nouveau projet" %} {% endif %} @@ -19,6 +20,7 @@ {% if project['id'] and project['id'] != '' %} {% endif %} +
+ {% endblock %} {% block content %} - - + + -

Charge pour tous les projets

-
+

Charge pour tous les projets

+
- {% for c in categories %} -

Charge pour la catégorie {{ c.name }}

-
- {% endfor %} +{% for c in categories %} +

Charge pour la catégorie {{ c.name }}

+
+{% endfor %} {% endblock %} {% block more_scripts %} - {% include 'd3js-includes.html' %} - {% include 'charges-includes.html' %} - - -{% endblock %} +{% include 'hg-includes.html' %} +{# {% include 'd3js-includes.html' %}#} +{% include 'charges-includes.html' %} + + +{% endblock %} \ No newline at end of file diff --git a/app/models.py b/app/models.py index 34f910d..d8b51a8 100644 --- a/app/models.py +++ b/app/models.py @@ -12,7 +12,7 @@ db = SQLAlchemy() class Formable: """ - Parent class allowing some html form facilities + Parent class allowing some html form facilities """ export_keys = [] @@ -32,7 +32,7 @@ class Formable: """ Export the orm object to a structure easily used in jinja - :return: nothing + :return: nothing """ # first set default keys @@ -43,21 +43,19 @@ class Formable: _value = getattr(self, key) _struct[key] = '' if _value is None else _value return _struct +""" + Categorized projects + There is one label list, + each label belongs to one or more categories. + + The projects are labelled by one or more label. + + Thus this is modeled with classes + Project, Label and Category + And many_to_many association are done through + ProjectLabel and CategoryLabel """ -# -# Categorized projects -# -# There is one label list, -# each label belongs to one or more categories. -# -# The projects are labelled by one or more label. -# -# Thus this is modeled with classes -# Project, Label and Category -# And many_to_many association are done through -# ProjectLabel and CategoryLabel -# class ProjectStatus(db.Model, Formable): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), unique=True) diff --git a/app/templates/base_page.html b/app/templates/base_page.html index fec6dc4..f9f80b5 100644 --- a/app/templates/base_page.html +++ b/app/templates/base_page.html @@ -153,9 +153,12 @@ - + + + - diff --git a/app/templates/hg-includes.html b/app/templates/hg-includes.html index 57e316c..543360f 100644 --- a/app/templates/hg-includes.html +++ b/app/templates/hg-includes.html @@ -1,4 +1,8 @@ + + + + + type="text/javascript"> \ No newline at end of file diff --git a/tests/frontend_tests.py b/tests/frontend_tests.py index 6f8a492..0626a1c 100644 --- a/tests/frontend_tests.py +++ b/tests/frontend_tests.py @@ -7,6 +7,7 @@ from flask_testing import LiveServerTestCase from selenium import webdriver from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.select import Select +from selenium.webdriver.common.alert import Alert from app import create_app from app.models import Agent, Charge, Project, AgentResponsability, ProjectStatus, Label, Category, Capacity, \ @@ -124,7 +125,7 @@ class AccessTestCase(BaseFrontTestCase): self.driver.get(target_url) self.assertEqual(self.driver.current_url, f'http://localhost:8943/agent/{agent_id}') td_elmts = self.driver.find_elements_by_xpath("//table[contains(@class,'table_datatables')]/tbody/tr/td") - self.assertEqual(260, len(td_elmts)) + self.assertEqual(312, len(td_elmts)) def test_projects_page(self): target_url = self.get_server_url() + url_for('main.projects') @@ -360,7 +361,14 @@ class FormsTestCase(BaseFrontTestCase): self.assertTrue(project_name + "< existe déjà" in message.text) def test_project_delete(self): - self.check_delete(url_for('main.project_delete', project_id=1)) + delete_url = url_for('main.project_delete', project_id=1) + target_url = self.get_server_url() + delete_url + self.driver.get(target_url) + + self.driver.find_element_by_xpath("//input[@id='deleteButton']").click() + Alert(self.driver).accept() + message = self.driver.find_element_by_xpath("//div[@id='messages']") + self.assertTrue("supprimé" in message.text) def test_project_status_edit(self): init_dict = {'name': 'Abandonné'} -- libgit2 0.21.2