Commit 0011bed0cf4dd584f00e88ce1a9cdbfb178d8c87
Exists in
master
and in
4 other branches
Update category/label data model
Showing
11 changed files
with
270 additions
and
112 deletions
Show diff stats
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 | ... | ... |