Blame view

app/db_mgr.py 11.9 KB
02118cc9   hitier   Show projects cat...
1
from app.models import db, Period, Project, Category
1aeda847   hitier   Two db request wr...
2

0a8a3ec6   hitier   Convert charge fr...
3
4
5
6
7
8
9
# TODO: make this configurable, and choose another place to use it,
#       in 'routes.py' maybe
# Charge as stored on a 100% basis, percent of total worked time;
# Here it is possible to convert to an ETP basis, dividing by 100
# or we set it to '1' if we want to keep the percent basis
charge_unit = 100

1aeda847   hitier   Two db request wr...
10

1b42d082   hitier   New category_labe...
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
def category_labels():
    """
    Build a list of dicts of the categorized labels for form display:

    [{'name': 'Domaine',
      'labels': [{'id': 1, 'name': 'R&T'},
                 {'id': 3, 'name': 'Autres 2'},
                        .
                        .
                        .
                 {'id': 11, 'name': 'Instrumentation Sol'}],
      },
     {'name': 'Pôle',
      'labels': [{'id': 12, 'name': 'Astronomie'},
                 {'id': 14, 'name': 'Solaire'},
                        .
                        .
                        .
                 {'id': 21, 'name': 'Administration'}]
      }]


    :return:
    """
    _categories = []
    for _c in Category.query.all():
        _category = {'name': _c.name, 'labels': []}
        for _l in _c.labels:
            _category['labels'].append({'id': _l.label.id, 'name': _l.label.name})
        _categories.append(_category)
    return _categories


f8e1465a   hitier   New projects list...
44
45
def projects():
    """
40126f91   hitier   Update project ro...
46
47
48
49
50
51
52
    Build the list of all projects, with their charges for the current period

    Returns a table with headers in the first line:
    Id, Project name, Category_1, ... , Category_n, Total_Charge_for_the_period

    The category cells embed the list of the labels of the project for that category.

f8e1465a   hitier   New projects list...
53
54
    :return:
    """
8ffdad81   hitier   Show current peri...
55
    current_period_id = get_current_period()
f8e1465a   hitier   New projects list...
56
    sql_txt = """
02118cc9   hitier   Show projects cat...
57
            select p.id, IFNULL(sum(tc.charge_rate), 0) as total_charge
f8e1465a   hitier   New projects list...
58
            from project as p left join
8ffdad81   hitier   Show current peri...
59
               ( select c.project_id, c.charge_rate from charge c where c.period_id = {})
f8e1465a   hitier   New projects list...
60
61
62
63
            tc
            on p.id = tc.project_id
            group by p.id
            order by total_charge desc;
8ffdad81   hitier   Show current peri...
64
            """.format(current_period_id)
02118cc9   hitier   Show projects cat...
65
66
67
    projects_charges = db.session.execute(sql_txt).fetchall()
    # First row is table titles
    all_projects = [["Id", "Projet"]]
40126f91   hitier   Update project ro...
68
    # Add all categories as next table titles
9b43874a   hitier   Change project_st...
69
    # headers will then look like [["Id", "Projet", "Domaine", "Pôle"]]
40126f91   hitier   Update project ro...
70
71
    categories = Category.query.all()
    for c in categories:
02118cc9   hitier   Show projects cat...
72
        all_projects[0].append(c.name)
9b43874a   hitier   Change project_st...
73
74
    # then add charge title
    # headers become [["Id", "Projet", "Domaine", "Pôle", "Charge"]]
02118cc9   hitier   Show projects cat...
75
76
    all_projects[0].append("Charge")
    # Build the table row by row
40126f91   hitier   Update project ro...
77
78
    for _pc in projects_charges:
        p_id = _pc[0]
fed90dbd   hitier   Rewrite list inte...
79
        project = Project.query.get(int(p_id))
40126f91   hitier   Update project ro...
80
        # adding 2 first columns: id, name
fed90dbd   hitier   Rewrite list inte...
81
        project_row = [project.id, project.name]
40126f91   hitier   Update project ro...
82
83
        # then labels, many for each category
        for category in categories:
fed90dbd   hitier   Rewrite list inte...
84
85
86
87
88
89
90
91
            # 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.labels]
            project_labels = [_pl.label for _pl in project.labels]
            intersection_labels = list(set.intersection(set(project_labels), set(category_labels)))
            labels = [label.name for label in intersection_labels]
40126f91   hitier   Update project ro...
92
93
94
            project_row.append(labels)
        # then add total charge
        project_row.append(_pc[1])
02118cc9   hitier   Show projects cat...
95
        all_projects.append(project_row)
f8e1465a   hitier   New projects list...
96
97
    return all_projects

0d6506cb   hitier   New project_charg...
98

1aeda847   hitier   Two db request wr...
99
def agents():
64515ad9   hitier   New agent charges...
100
    """
8ffdad81   hitier   Show current peri...
101
    Build the list of all agents, with their charges for the current period
64515ad9   hitier   New agent charges...
102
103
    :return:
    """
8ffdad81   hitier   Show current peri...
104
    current_period_id = get_current_period()
e5449e3e   hitier   Fix wrong agents ...
105
    sql_txt = """
a27666a1   hitier   Fix project num i...
106
107
108
109
110
111
            select a.id, a.firstname, a.secondname,
                   IFNULL (sum(tc.charge_rate), 0) as total_charge,
                   count(distinct tc.project_id) as num_projects
            from agent as a
            left join
               ( select c.agent_id, c.charge_rate , c.project_id from charge c where c.period_id = {0})
e5449e3e   hitier   Fix wrong agents ...
112
113
114
115
            tc
            on a.id = tc.agent_id
            group by a.id
            order by total_charge desc;
8ffdad81   hitier   Show current peri...
116
            """.format(current_period_id)
2cb0a345   hitier   Add 2 columns in ...
117
118
    all_agents = db.session.execute(sql_txt).fetchall()

1aeda847   hitier   Two db request wr...
119
120
121
    return all_agents


0d6506cb   hitier   New project_charg...
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
def charges_by_project(project_id):
    """
    Build the flatten list of charges for one project, period by period
    :param project_id:
    :return:
    """

    req_sql = f"""
            select p.name as period_name,
                   a.firstname ||" "|| a.secondname as agent_name,
                   s.name as service_name,
                   c2.name as capacity_name,
                   c.charge_rate as charge_rate
            from charge as c
                     join period p on c.period_id = p.id
                     join project p2 on c.project_id = p2.id
                     join service s on c.service_id = s.id
                     join agent a on a.id = c.agent_id
                     join capacity c2 on c2.id = c.capacity_id
            where project_id = {project_id}
            order by c.period_id
            """
0d6506cb   hitier   New project_charg...
144
145
146
147
148
149
150
    req_res = db.session.execute(req_sql)
    results = list(req_res)
    # Remove comma
    nocomma_results = []
    for line in results:
        nocomma_line = [str(cell) for cell in line]
        nocomma_results.append(nocomma_line)
b9d90810   hitier   Tweak charges tab...
151
    headers = ["Periode", "Agent", "Service", "Fonction", "Charge"]
0d6506cb   hitier   New project_charg...
152
153
154
155
    nocomma_results.insert(0, headers)
    return nocomma_results


90b18951   hitier   New db method: ch...
156
157
158
159
160
161
162
def charges_by_project_stacked(project_id, category="service"):
    """
    Build the list of charges for one project, period by period
    :param project_id: the project's id we want to return data for
    :param category: what dict to build for each period, 'service' or 'capacity' ?
    :return: a 2 dim table with header as first line and datas next, of the form
            period, category_0, category_1, ....., category_n,
9705f04b   hitier   Fix project charg...
163
164
            per_0, value_00, value_01, ....., value_0n,
            per_1, value_10, value_11, ....., value_1n,
90b18951   hitier   New db method: ch...
165
166
                .
                .
9705f04b   hitier   Fix project charg...
167
            per_n, value_n0, value_n1, ....., value_nn,
0a8a3ec6   hitier   Convert charge fr...
168
169

    TODO: common with charges_by_agent_stacked(agent_id): code to extrat
90b18951   hitier   New db method: ch...
170
    """
cf88ff52   hitier   Fix head data sel...
171
    if category == 'capacity':
40302c4f   hitier   Fix category tabl...
172
        category_table = 'capacity'
90b18951   hitier   New db method: ch...
173
        sql_req = """
9705f04b   hitier   Fix project charg...
174
        select sum(c.charge_rate)
90b18951   hitier   New db method: ch...
175
176
        from capacity c1 left join
            charge c on c1.id = c.capacity_id and project_id = {} and period_id={}
9705f04b   hitier   Fix project charg...
177
            group by c1.id
90b18951   hitier   New db method: ch...
178
179
        order by c1.id
        """
cf88ff52   hitier   Fix head data sel...
180
    elif category == 'service':
40302c4f   hitier   Fix category tabl...
181
        category_table = 'service'
90b18951   hitier   New db method: ch...
182
        sql_req = """
9705f04b   hitier   Fix project charg...
183
        select sum(c.charge_rate)
90b18951   hitier   New db method: ch...
184
185
        from service s left join
            charge c on s.id = c.service_id and project_id = {} and period_id={}
9705f04b   hitier   Fix project charg...
186
            group by s.id
90b18951   hitier   New db method: ch...
187
188
189
190
        order by s.id
        """
    else:
        raise ValueError("Waiting for 'service' or 'capacity'")
40302c4f   hitier   Fix category tabl...
191
    categories_req = "select name from {} order by id".format(category_table)
90b18951   hitier   New db method: ch...
192
    categories = [c for (c,) in db.session.execute(categories_req)]
b48d8e69   hitier   Changed charges_b...
193
    headers = ['period'] + categories
90b18951   hitier   New db method: ch...
194
195
196
197
198
199
200
    all_charges = [headers]
    for (period_id, period_name) in db.session.execute("select id, name from period order by id"):
        # build the request from the string previously set
        charge_by_categorie_req = sql_req.format(project_id, period_id)
        # build the charges line for the current period
        category_charges = [period_name]
        for (category_rate,) in db.session.execute(charge_by_categorie_req):
0a8a3ec6   hitier   Convert charge fr...
201
            category_rate = str(round(category_rate / charge_unit, 2)) if category_rate else '0'
90b18951   hitier   New db method: ch...
202
203
204
205
206
207
            category_charges.append(category_rate)
        all_charges.append(category_charges)

    return all_charges


95880b0d   hitier   New charges_for_p...
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
def charges_for_projects_stacked():
    sql_req = """
    select p.name, sum(charge_rate) as tot_charg
    from charge
             join project p on p.id = charge.project_id
    where period_id = {}
    group by project_id
    """
    projects_req = "select name from project order by id"
    projects_names = [name for (name,) in db.session.execute(projects_req)]
    headers = ['period'] + projects_names
    all_charges = [headers]
    for (period_id, period_name) in db.session.execute("select id, name from period order by id"):
        project_charges = [period_name]
        charges_for_projects_req = sql_req.format(period_id)
a27666a1   hitier   Fix project num i...
223
        for (project_name, project_charge) in db.session.execute(charges_for_projects_req):
95880b0d   hitier   New charges_for_p...
224
225
226
227
228
229
            project_charge = str(round(project_charge / charge_unit, 2)) if project_charge else '0'
            project_charges.append(project_charge)
        all_charges.append(project_charges)
    return all_charges


b48d8e69   hitier   Changed charges_b...
230
231
232
def charges_by_agent_stacked(agent_id):
    """
    Build the list of charges for all projects of one agent, period by period
0a8a3ec6   hitier   Convert charge fr...
233
    TODO: common with charges_by_project_stacked(project_id, ..) code to extrat
b48d8e69   hitier   Changed charges_b...
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
    :param agent_id:
    :return:
    """
    categories_req = "select name from project order by id"
    categories = [c for (c,) in db.session.execute(categories_req)]
    headers = ['period'] + categories
    all_charges = [headers]
    for (period_id, period_name) in db.session.execute("select id, name from period order by id"):
        charge_by_project_req = """
                               select sum(c.charge_rate)
                                from project p
                                         left join
                                     charge c on p.id = c.project_id and agent_id = {} and period_id = {}
                                group by p.id
                                order by p.id 
                            """.format(agent_id, period_id)
b48d8e69   hitier   Changed charges_b...
250
251
        category_charges = [period_name]
        for (category_rate,) in db.session.execute(charge_by_project_req):
0a8a3ec6   hitier   Convert charge fr...
252
            category_rate = str(round(category_rate / charge_unit, 2)) if category_rate else '0'
b48d8e69   hitier   Changed charges_b...
253
254
255
256
257
            category_charges.append(category_rate)
        all_charges.append(category_charges)
    return all_charges


64515ad9   hitier   New agent charges...
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
def charges_by_agent_tabled(agent_id):
    """
    Build the flatten list of charges for one agent, period by period
    :param agent_id:
    :return:
    """
    req_sql = f"""
            select p.name as period_name,
                   p2.name as project_name,
                   s.name as service_name,
                   c2.name as capacity_name,
                   c.charge_rate as charge_rate
            from charge as c
                     join period p on c.period_id = p.id
                     join project p2 on c.project_id = p2.id
                     join service s on c.service_id = s.id
                     join agent a on a.id = c.agent_id
                     join capacity c2 on c2.id = c.capacity_id
            where agent_id = {agent_id}
            order by c.period_id
            """
    req_res = db.session.execute(req_sql)
    results = list(req_res)
    # Remove comma
    nocomma_results = []
    for line in results:
70da5dd5   hitier   Insert charges ta...
284
        nocomma_line = [str(cell) for cell in line]
64515ad9   hitier   New agent charges...
285
        nocomma_results.append(nocomma_line)
b9d90810   hitier   Tweak charges tab...
286
    headers = ["Periode", "Projet", "Service", "Fonction", "Charge"]
64515ad9   hitier   New agent charges...
287
288
289
290
    nocomma_results.insert(0, headers)
    return nocomma_results


1aeda847   hitier   Two db request wr...
291
def charges_by_agent(agent_id):
8b3ab81d   hitier   All charges now i...
292
293
    periods = db.session.execute("select name from period")
    sql_txt = "select p.name, sum(charge_rate)  from charge inner join period p on charge.period_id = p.id\
71b91c22   hitier   New json route fo...
294
295
                where agent_id={} group by period_id order by p.name" \
        .format(agent_id)
1aeda847   hitier   Two db request wr...
296
    request = db.session.execute(sql_txt)
8b3ab81d   hitier   All charges now i...
297
298
299
300
301
302
303
304
    all_charges = []
    charges_list = [(period, charge) for (period, charge) in request.fetchall()]
    charges_dict = dict(charges_list)
    for (p,) in periods:
        if p in charges_dict.keys():
            all_charges.append([p, charges_dict[p]])
        else:
            all_charges.append([p, 0])
71b91c22   hitier   New json route fo...
305
    return all_charges
aec65e04   hitier   New cli command g...
306
307
308
309
310
311
312


def get_current_period():
    """
    :return:  the id of the period of current day
    """
    # TODO: request on dates as soon as periods are dated
083ac872   hitier   Return period id ...
313
314
    p = Period.query.filter((Period.name == '2021') | (Period.name == '2021_S1')).one()
    return p.id