Commit 0011bed0cf4dd584f00e88ce1a9cdbfb178d8c87

Authored by hitier
2 parents e20cd5d4 74c7dad5

Update category/label data model

app/commands/commands.py
... ... @@ -10,7 +10,7 @@ from sqlalchemy.exc import IntegrityError
10 10 from sqlalchemy.sql import func
11 11  
12 12 from app.models import db, Agent, Service, Project, Capacity, Period, Charge, AgentStatus, Company, AgentBap, \
13   - AgentGrade, Category, Label, ProjectLabel
  13 + AgentGrade, Category, Label, ProjectLabel, CategoryLabel
14 14 # TODO: rename to methods and add get_roles()
15 15 from app.auth.models import User, _nameToRole, _roleToName
16 16  
... ... @@ -79,15 +79,22 @@ def feed_from_irap(csv_file_name):
79 79 grades = []
80 80 companies = []
81 81 statuses = []
  82 + labels = []
82 83  
83   - # build a category dict of lists
  84 + # Build a category dict of lists
84 85 # key being the category name,
85 86 # list being filled with corresponding labels
86   - categories = {k: [] for k in categorie_keys}
  87 + categorie_labels = {k: [] for k in categorie_keys}
87 88  
  89 + # Projects' labels is a dict of lists
  90 + # indexed by project name
  91 + # containing labels for that project
88 92 #
89   - categorized_projects = {}
  93 + project_labels = {}
90 94  
  95 + #
  96 + # Parse the rows and fill in various lists
  97 + #
91 98 for r in rows:
92 99 services.append(r[service_key])
93 100 baps.append(r[bap_key])
... ... @@ -95,16 +102,20 @@ def feed_from_irap(csv_file_name):
95 102 companies.append(r[company_key])
96 103 statuses.append(r[status_key])
97 104  
98   - # categorized_projects
99   - project = r[project_key]
100   - categorized_projects[project] = []
  105 + # the projet and its labels
  106 + project_name = r[project_key]
  107 + project_labels[project_name] = []
101 108  
102   - # fill in the category-labels dict
  109 + # now fill in both
  110 + # the labels list,
  111 + # the category-labels dict,
  112 + # and the project-labels dict
103 113 for k in categorie_keys:
104   - categories[k].append(r[k])
105   - categorized_projects[project].append(r[k])
  114 + labels.append(r[k])
  115 + categorie_labels[k].append(r[k])
  116 + project_labels[project_name].append(r[k])
106 117  
107   - # build agents list of dicts
  118 + # create the agents list of dicts
108 119 agents.append({
109 120 'firstname': r[firstname_key],
110 121 'secondname': r[secondname_key],
... ... @@ -124,25 +135,25 @@ def feed_from_irap(csv_file_name):
124 135 #
125 136 # 1- first remove empty string with filter()
126 137 # 2- then keep only uniq item with set()
127   - # 3- at last alpha sort with sorted()
  138 + # 3- at last alpha sort with sorted()
128 139 #
129 140 services = sorted(set(filter(None, services)))
130 141 baps = sorted(set(filter(None, baps)))
131 142 grades = sorted(set(filter(None, grades)))
132 143 companies = sorted(set(filter(None, companies)))
133 144 statuses = sorted(set(filter(None, statuses)))
  145 + labels = sorted(set(filter(None, labels)))
134 146  
135   - # Do the same for the projects, that are keys of categorized_projects
  147 + # Do the same for the projects, that are keys of project_labels
136 148 #
137   - projects = sorted(set(categorized_projects.keys()))
  149 + projects = sorted(set(project_labels.keys()))
138 150  
139   - # Do the same for the category labels stored as a dict
  151 + # Do the same for the labels inside each category
  152 + # c is the category name containing the labels list
140 153 #
141   - # k is category name
142   - # v is labels list for that category
143   - for k in categorie_keys:
144   - labels = sorted(set(filter(None, categories[k])))
145   - categories[k] = labels
  154 + for c in categorie_keys:
  155 + c_labels = sorted(set(filter(None, categorie_labels[c])))
  156 + categorie_labels[c] = c_labels
146 157  
147 158 # At least, as agents is a list of dicts, sorting is a bit tricky
148 159 #
... ... @@ -153,6 +164,12 @@ def feed_from_irap(csv_file_name):
153 164 agents = list({(a['firstname'], a['secondname']): a for a in agents}.values())
154 165 agents = sorted(agents, key=lambda a: (a['firstname'], a['secondname']))
155 166  
  167 + #
  168 + # We are done with collecting data
  169 + #
  170 + # Now we write to database
  171 + #
  172 +
156 173 # Feed baps from column
157 174 #
158 175 for b in baps:
... ... @@ -188,6 +205,20 @@ def feed_from_irap(csv_file_name):
188 205 db.session.add(n_p)
189 206 db.session.commit()
190 207  
  208 + # Feed labels from column
  209 + #
  210 + for _l in labels:
  211 + n_l = Label(name=_l)
  212 + db.session.add(n_l)
  213 + db.session.commit()
  214 +
  215 + # Feed categories from initial list
  216 + #
  217 + for _c in categorie_keys:
  218 + n_c = Category(name=_c)
  219 + db.session.add(n_c)
  220 + db.session.commit()
  221 +
191 222 # Feed services from column
192 223 #
193 224 for s in services:
... ... @@ -212,25 +243,25 @@ def feed_from_irap(csv_file_name):
212 243  
213 244 # Feed categories and labels
214 245 #
215   - for c, l in categories.items():
216   - current_app.logger.debug("Adding category " + c)
217   - n_c = Category(name=c.capitalize())
218   - db.session.add(n_c)
219   - # db.session.commit()
220   - for label in l:
221   - current_app.logger.debug("Adding label {} for id {}".format(label, n_c.id))
222   - n_l = Label(name=label, category=n_c)
223   - db.session.add(n_l)
  246 + for category, labels in categorie_labels.items():
  247 + print(category)
  248 + n_c = Category.query.filter_by(name=category).one()
  249 + for label in labels:
  250 + print(label)
  251 + n_l = Label.query.filter(Label.name == label).one()
  252 + current_app.logger.debug(f"Adding label {label} to category {category}")
  253 + n_cl = CategoryLabel(label=n_l, category=n_c)
  254 + db.session.add(n_cl)
224 255 db.session.commit()
225 256  
226 257 # Feed project's labels
227 258 #
228   - for p, labels in categorized_projects.items():
229   - n_p = Project.query.filter(Project.name == p).one()
230   - for l in labels:
231   - n_l = Label.query.filter(Label.name == l).one()
232   - n_c = n_l.category
233   - n_pl = ProjectLabel(project=n_p, category=n_c, label=n_l)
  259 + for project, labels in project_labels.items():
  260 + print(f"Project {project}")
  261 + n_p = Project.query.filter(Project.name == project).one()
  262 + for label in labels:
  263 + n_l = Label.query.filter(Label.name == label).one()
  264 + n_pl = ProjectLabel(project=n_p, label=n_l)
234 265 db.session.add(n_pl)
235 266 db.session.commit()
236 267  
... ... @@ -313,8 +344,9 @@ def feed_from_lesia():
313 344 domain_category = Category(name="Domaine")
314 345 domains = lesia_session.query(lesia_domains)
315 346 for d in domains:
316   - n_l = Label(name=d.nom, category=domain_category)
317   - db.session.add(n_l)
  347 + n_l = Label(name=d.nom)
  348 + n_cl = CategoryLabel(category=domain_category, label=n_l)
  349 + db.session.add(n_cl)
318 350 db.session.commit()
319 351  
320 352 # Feed all lesia 'pôle' names and link to new category "Pôle"
... ... @@ -322,8 +354,9 @@ def feed_from_lesia():
322 354 pole_category = Category(name="Pôle")
323 355 poles = lesia_session.query(lesia_poles)
324 356 for p in poles:
325   - n_l = Label(name=p.nom, category=pole_category)
326   - db.session.add(n_l)
  357 + n_l = Label(name=p.nom)
  358 + n_cl = CategoryLabel(category=pole_category, label=n_l)
  359 + db.session.add(n_cl)
327 360 db.session.commit()
328 361  
329 362 # Feed lesia project with proper "pôle"
... ... @@ -338,7 +371,7 @@ def feed_from_lesia():
338 371 pole_name = lesia_session.query(lesia_poles).filter(lesia_poles.id == p.pole_id).one().nom
339 372 # search corresponding Label and store in ProjectLabel table
340 373 n_l = Label.query.filter(Label.name == pole_name).one()
341   - n_pl = ProjectLabel(project=n_p, category=n_l.category, label=n_l)
  374 + n_pl = ProjectLabel(project=n_p, label=n_l)
342 375 db.session.add(n_pl)
343 376 db.session.commit()
344 377  
... ... @@ -350,7 +383,7 @@ def feed_from_lesia():
350 383 domain_name = lesia_session.query(lesia_domains).filter(lesia_domains.id == dp.domaine_id).one().nom
351 384 n_p = Project.query.filter(Project.name == project_name).one()
352 385 n_l = Label.query.filter(Label.name == domain_name).one()
353   - n_pl = ProjectLabel(project=n_p, category=n_l.category, label=n_l)
  386 + n_pl = ProjectLabel(project=n_p, label=n_l)
354 387 db.session.add(n_pl)
355 388 # Some projects have 2 domain labels in lesia db
356 389 # That is not allowed any more in the new model.
... ...
app/db_mgr.py
... ... @@ -10,7 +10,13 @@ charge_unit = 100
10 10  
11 11 def projects():
12 12 """
13   - Build the list of all agents, with their charges for the current period
  13 + Build the list of all projects, with their charges for the current period
  14 +
  15 + Returns a table with headers in the first line:
  16 + Id, Project name, Category_1, ... , Category_n, Total_Charge_for_the_period
  17 +
  18 + The category cells embed the list of the labels of the project for that category.
  19 +
14 20 :return:
15 21 """
16 22 current_period_id = get_current_period()
... ... @@ -26,21 +32,33 @@ def projects():
26 32 projects_charges = db.session.execute(sql_txt).fetchall()
27 33 # First row is table titles
28 34 all_projects = [["Id", "Projet"]]
29   - # Add all categories as last table titles
30   - for c in Category.query.all():
  35 + # Add all categories as next table titles
  36 + # headers will then look like [["Id", "Projet", "Domaine", "Pôle"]]
  37 + categories = Category.query.all()
  38 + for c in categories:
31 39 all_projects[0].append(c.name)
  40 + # then add charge title
  41 + # headers become [["Id", "Projet", "Domaine", "Pôle", "Charge"]]
32 42 all_projects[0].append("Charge")
33 43 # Build the table row by row
34   - for pc in projects_charges:
35   - p_id = pc[0]
36   - p = Project.query.get(int(p_id))
37   - p_labels = p.labels
38   - # adding 3 first columns: id, name and total charge
39   - project_row = [p.id, p.name]
40   - # then labels, one for each category
41   - for pl in p_labels:
42   - project_row.append(pl.label.name)
43   - project_row.append(pc[1])
  44 + for _pc in projects_charges:
  45 + p_id = _pc[0]
  46 + project = Project.query.get(int(p_id))
  47 + # adding 2 first columns: id, name
  48 + project_row = [project.id, project.name]
  49 + # then labels, many for each category
  50 + for category in categories:
  51 + # we build the labels.name list of the intersection of current category's labels with project's labels
  52 + # Comprehensive shortcut is:
  53 + # labels = [_cl.label.name for _cl in category.labels if _cl.label in [_pl.label for _pl in project.labels]]
  54 + #
  55 + category_labels = [_cl.label for _cl in category.labels]
  56 + project_labels = [_pl.label for _pl in project.labels]
  57 + intersection_labels = list(set.intersection(set(project_labels), set(category_labels)))
  58 + labels = [label.name for label in intersection_labels]
  59 + project_row.append(labels)
  60 + # then add total charge
  61 + project_row.append(_pc[1])
44 62 all_projects.append(project_row)
45 63 return all_projects
46 64  
... ...
app/main/routes.py
... ... @@ -91,7 +91,7 @@ def project(project_id):
91 91 this_project = Project.query.get(int(project_id))
92 92 charges_table = db_mgr.charges_by_project(project_id)
93 93 return render_template('project.html',
94   - project=this_project,
  94 + project=this_project.to_struct(),
95 95 charges=charges_table,
96 96 subtitle="{}".format(this_project.name))
97 97  
... ...
app/main/templates/project.html
... ... @@ -15,10 +15,10 @@
15 15 <div class="card-body">
16 16 <dl class="row">
17 17 <dt class="col-sm-2 text-right">ID :</dt>
18   - <dd class="col-sm-10 text-left">{{project.id}}</dd>
19   - {% for pl in project.labels %}
20   - <dt class="col-sm-2 text-right">{{pl.label.category.name}} :</dt>
21   - <dd class="col-sm-10 text-left">{{pl.label.name}}</dd>
  18 + <dd class="col-sm-10 text-left">{{project['id']}}</dd>
  19 + {% for category, labels in project['category_labels'].items() %}
  20 + <dt class="col-sm-2 text-right">{{category}} :</dt>
  21 + <dd class="col-sm-10 text-left">{{labels|join(",")}}</dd>
22 22 {% endfor %}
23 23 <dt class="col-sm-2 text-right">Etat :</dt>
24 24 <dd class="col-sm-10 text-left"></dd>
... ...
app/main/templates/projects.html
... ... @@ -18,8 +18,9 @@
18 18 <td><a href="{{url_for('main.project', project_id=project[0])}}">
19 19 {{ project[1] }}</a>
20 20 </td>
  21 + {#the category cells#}
21 22 {% for c in project[2:-1] %}
22   - <td>{{c}}</td>
  23 + <td>{{c|join(',')}}</td>
23 24 {% endfor %}
24 25 <td>{{ project[-1] /100}}</td>
25 26 </tr>
... ...
app/models.py
... ... @@ -41,36 +41,82 @@ class Formable:
41 41 #
42 42 # Categorized projects
43 43 #
  44 +# There is one label list,
  45 +# each label belongs to one or more categories.
  46 +#
  47 +# The projects are labelled by one or more label.
  48 +#
  49 +# Thus this is modeled with classes
  50 +# Project, Label and Category
  51 +# And many_to_many association are done through
  52 +# ProjectLabel and CategoryLabel
  53 +#
44 54  
45   -class Project(db.Model):
  55 +class Project(db.Model, Formable):
46 56 id = db.Column(db.Integer, primary_key=True)
47 57 name = db.Column(db.String)
48 58 labels = relationship("ProjectLabel", back_populates="project")
49 59  
  60 + export_keys = ['name']
50 61  
51   -class Category(db.Model):
  62 + def to_struct(self):
  63 + """
  64 + overide parent method to include one key: agent.fullname
  65 +
  66 + Mainly we add the 'labels' element containing a dict
  67 + where category key contains labels for that project.
  68 + :return:
  69 + """
  70 + _struct = super(Project, self).to_struct()
  71 + _struct['category_labels'] = {}
  72 + for _c in Category.query.all():
  73 + category_labels = []
  74 + for _l in self.labels:
  75 + if _l.label in [_cl.label for _cl in _c.labels]:
  76 + category_labels.append(_l.label.name)
  77 + _struct['category_labels'][_c.name] = category_labels
  78 + return _struct
  79 +
  80 +
  81 +class ProjectLabel(db.Model):
  82 + """
  83 + Labelling projects.
  84 + On project can have many labels.
  85 + And one label will be set to many projects
  86 + """
52 87 id = db.Column(db.Integer, primary_key=True)
53   - name = db.Column(db.String)
54   - labels = relationship("Label", back_populates="category")
55   - projects = relationship("ProjectLabel", back_populates="category")
  88 + project_id = db.Column(db.Integer, db.ForeignKey('project.id'))
  89 + label_id = db.Column(db.Integer, db.ForeignKey('label.id'))
  90 + project = relationship("Project", back_populates="labels")
  91 + label = relationship("Label", back_populates="projects")
56 92  
57 93  
58 94 class Label(db.Model):
59 95 id = db.Column(db.Integer, primary_key=True)
60 96 name = db.Column(db.String, unique=True)
61 97 category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
62   - category = relationship("Category", back_populates="labels")
63 98 projects = relationship("ProjectLabel", back_populates="label")
  99 + categories = relationship("CategoryLabel", back_populates="label")
64 100  
65 101  
66   -class ProjectLabel(db.Model):
67   - project_id = db.Column(db.Integer, db.ForeignKey('project.id'), primary_key=True)
68   - category_id = db.Column(db.Integer, db.ForeignKey('category.id'), primary_key=True)
  102 +class CategoryLabel(db.Model):
  103 + """
  104 + Categorizing labels:
  105 + one label can be added to many categories
  106 + one category hosts many labels
  107 + """
  108 + id = db.Column(db.Integer, primary_key=True)
  109 + category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
69 110 label_id = db.Column(db.Integer, db.ForeignKey('label.id'))
  111 + category = relationship("Category", back_populates="labels")
  112 + label = relationship("Label", back_populates="categories")
70 113  
71   - project = relationship("Project", back_populates="labels")
72   - category = relationship("Category", back_populates="projects")
73   - label = relationship("Label", back_populates="projects")
  114 +
  115 +class Category(db.Model):
  116 + id = db.Column(db.Integer, primary_key=True)
  117 + name = db.Column(db.String)
  118 + labels = relationship("Label", back_populates="category")
  119 + labels = relationship("CategoryLabel", back_populates="category")
74 120  
75 121  
76 122 #
... ...
resources/lesia-btp.sqlite
No preview for this file type
tests/backend_tests.py
1 1 import os
2 2 import unittest
  3 +from pprint import pprint
3 4  
4 5 from pdc_config import TestConfig
5 6 from app import create_app, db_mgr
... ... @@ -40,6 +41,22 @@ class DbMgrTestCase(BaseTestCase):
40 41 all_projects = db_mgr.projects()
41 42 self.assertEqual(5, len(all_projects[0]))
42 43  
  44 + def test_projects_all_columns(self):
  45 + all_projects = db_mgr.projects()
  46 + for p in all_projects:
  47 + with self.subTest(p):
  48 + self.assertEqual(5, len(p), f"Failed on {p[1]}")
  49 +
  50 + def test_projects_labels(self):
  51 + self.assertEqual(0, 0)
  52 + all_projects = db_mgr.projects()
  53 + # test all projects but skip headers first row
  54 + for p in all_projects[1:]:
  55 + with self.subTest(p):
  56 + # the number of labels for each category and any project should be less than 2
  57 + self.assertTrue(len(p[2]) <= 2, f"Failed on {p[1]}: " + ", ".join(p[2]))
  58 + self.assertTrue(len(p[3]) <= 2, f"Failed on {p[1]}: " + ", ".join(p[3]))
  59 +
43 60 def test_agents(self):
44 61 all_agents = db_mgr.agents()
45 62 self.assertEqual(548, len(all_agents))
... ...
tests/common_db_feed.py
... ... @@ -14,41 +14,42 @@ def resources_to_instancedb(app):
14 14 copyfile(sqlite_file_name, db_path)
15 15 return db_path
16 16  
17   -
18   -categorized_labels = {'pole': ['Spatial', 'Sol'], 'domaine': ['soleil-terre', 'atmosphere', 'r&t', 'géologie']}
19   -projects_categories = {'ChemCam': {'pole': 'Spatial', 'domaine': 'géologie'},
20   - 'Pilot': {'pole': 'Spatial', 'domaine': 'atmosphere'},
21   - 'Ambre': {'pole': 'Spatial', 'domaine': 'soleil-terre'}}
22   -
23   -
24   -def feed_projects():
25   - for c_name, labels in categorized_labels.items():
26   - n_c = Category(name=c_name)
27   - db.session.add(n_c)
28   - for l_name in labels:
29   - n_l = Label(name=l_name, category=n_c)
30   - db.session.add(n_l)
31   -
32   - db.session.commit()
33   -
34   - project_names = projects_categories.keys()
35   -
36   - for p_name in set(project_names):
37   - n_p = Project(name=p_name)
38   - db.session.add(n_p)
39   - db.session.commit()
40   -
41   - for p, categories in projects_categories.items():
42   - n_p = db.session.query(Project).filter(Project.name == p).one()
43   - for c_name, l_name in categories.items():
44   - n_c = db.session.query(Category).filter(Category.name == c_name).one()
45   - n_l = db.session.query(Label).filter(Label.name == l_name).one()
46   - n_pc = ProjectLabel(project=n_p, category=n_c, label=n_l)
47   - db.session.add(n_pc)
48   -
49   - db.session.commit()
50   -
51   -
52   -def show_categories():
53   - for c in db.session.query(Category).all():
54   - print(c.name + ": ", ",".join([l.name for l in c.categorized_labels]))
  17 +# TODO: rewrite for new category/label model
  18 +#
  19 +# categorized_labels = {'pole': ['Spatial', 'Sol'], 'domaine': ['soleil-terre', 'atmosphere', 'r&t', 'géologie']}
  20 +# projects_categories = {'ChemCam': {'pole': 'Spatial', 'domaine': 'géologie'},
  21 +# 'Pilot': {'pole': 'Spatial', 'domaine': 'atmosphere'},
  22 +# 'Ambre': {'pole': 'Spatial', 'domaine': 'soleil-terre'}}
  23 +#
  24 +#
  25 +# def feed_projects():
  26 +# for c_name, labels in categorized_labels.items():
  27 +# n_c = Category(name=c_name)
  28 +# db.session.add(n_c)
  29 +# for l_name in labels:
  30 +# n_l = Label(name=l_name, category=n_c)
  31 +# db.session.add(n_l)
  32 +#
  33 +# db.session.commit()
  34 +#
  35 +# project_names = projects_categories.keys()
  36 +#
  37 +# for p_name in set(project_names):
  38 +# n_p = Project(name=p_name)
  39 +# db.session.add(n_p)
  40 +# db.session.commit()
  41 +#
  42 +# for p, categories in projects_categories.items():
  43 +# n_p = db.session.query(Project).filter(Project.name == p).one()
  44 +# for c_name, l_name in categories.items():
  45 +# n_c = db.session.query(Category).filter(Category.name == c_name).one()
  46 +# n_l = db.session.query(Label).filter(Label.name == l_name).one()
  47 +# n_pc = ProjectLabel(project=n_p, category=n_c, label=n_l)
  48 +# db.session.add(n_pc)
  49 +#
  50 +# db.session.commit()
  51 +#
  52 +#
  53 +# def show_categories():
  54 +# for c in db.session.query(Category).all():
  55 +# print(c.name + ": ", ",".join([l.name for l in c.categorized_labels]))
... ...
tests/db_tests.py
1 1 import os
2 2 import unittest
  3 +from pprint import pprint
3 4  
4 5 from app import create_app, db, User
5 6 from app.models import Project
6 7 from pdc_config import TestConfig
7   -from tests.common_db_feed import feed_projects, resources_to_instancedb
  8 +from tests.common_db_feed import resources_to_instancedb
8 9  
9 10  
10 11 class DbBaseTestCase(unittest.TestCase):
... ... @@ -36,7 +37,8 @@ class DbBaseTestCase(unittest.TestCase):
36 37 #
37 38 if self.mode == 'memory':
38 39 db.create_all()
39   - feed_projects()
  40 + # TODO: to be rewriten for new category/label model
  41 + # feed_projects()
40 42  
41 43 def tearDown(self):
42 44 if self.mode == 'memory':
... ... @@ -48,6 +50,21 @@ class DbBaseTestCase(unittest.TestCase):
48 50 self.app_context.pop()
49 51  
50 52  
  53 +class AnyModelTestCase(DbBaseTestCase):
  54 + def setUp(self):
  55 + DbBaseTestCase.setUp(self, 'btp')
  56 +
  57 + def tearDown(self):
  58 + DbBaseTestCase.tearDown(self)
  59 +
  60 + def test_projects_struct(self):
  61 + for project in Project.query.all():
  62 + with self.subTest(project):
  63 + categories = list(project.to_struct()['category_labels'].keys())
  64 + self.assertEqual(['Domaine', 'Pôle'], categories,
  65 + f"Failed on {project.name} categories: {categories}")
  66 +
  67 +
51 68 class ChargeModelTestCase(DbBaseTestCase):
52 69 def setUp(self):
53 70 DbBaseTestCase.setUp(self, 'btp')
... ...
tests/frontend_tests.py
... ... @@ -112,6 +112,20 @@ class AccessTestCase(BaseFrontTestCase):
112 112 self.driver.get(target_url)
113 113 self.assertEqual(self.driver.current_url, 'http://localhost:8943/projects')
114 114  
  115 + def test_projects_trs(self):
  116 + target_url = self.get_server_url() + url_for('main.projects')
  117 + self.driver.get(target_url)
  118 + td_elmts = self.driver.find_elements_by_xpath("//table/tbody/tr")
  119 + self.assertEqual(101, len(td_elmts))
  120 +
  121 + def test_projects_tds(self):
  122 + target_url = self.get_server_url() + url_for('main.projects')
  123 + self.driver.get(target_url)
  124 + for tr in self.driver.find_elements_by_xpath("//table/tbody/tr"):
  125 + with self.subTest(tr):
  126 + tds = tr.find_elements_by_tag_name('td')
  127 + self.assertEqual(4, len(tds), tds[0].text + " has wrong number of columns")
  128 +
115 129 def test_project_page(self):
116 130 project_id = 8
117 131 target_url = self.get_server_url() + url_for('main.project', project_id=project_id)
... ... @@ -120,6 +134,17 @@ class AccessTestCase(BaseFrontTestCase):
120 134 td_elmts = self.driver.find_elements_by_xpath("//table[@id='charge_table']/tbody/tr/td")
121 135 self.assertEqual(1085, len(td_elmts))
122 136  
  137 + def test_project_page_categories(self):
  138 + project_id = 84
  139 + target_url = self.get_server_url() + url_for('main.project', project_id=project_id)
  140 + self.driver.get(target_url)
  141 + self.assertEqual(self.driver.current_url, f'http://localhost:8943/project/{project_id}')
  142 + # pole_dt_db = self.driver.find_element_by_xpath("//dt[text()='Pôle']/following-sibling::dd")
  143 + pole_dt_db = self.driver.find_element_by_xpath("//dt[contains(text(),'Pôle')]/following-sibling::dd")
  144 + self.assertEqual('Solaire', pole_dt_db.text)
  145 + domaine_dt_db = self.driver.find_element_by_xpath("//dt[contains(text(),'Domaine')]/following-sibling::dd")
  146 + self.assertTrue('LESIA' in domaine_dt_db.text)
  147 +
123 148  
124 149 class FormsTestCase(BaseFrontTestCase):
125 150  
... ...