Skip to content

visualizations.slides

generate_results_slides(instance)

Function to generate the results slides for a particular problem instance with solution

Source code in afccp/visualizations/slides.py
 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
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
def generate_results_slides(instance):
    """
    Function to generate the results slides for a particular problem instance with solution
    """

    # Shorthand
    mdl_p = instance.mdl_p

    # Build the presentation object
    prs = Presentation(afccp.globals.paths['files'] + 'results_slide_template.pptx')

    # Delete the current slides in the template
    for i in range(len(prs.slides) - 1, -1, -1):
        rId = prs.slides._sldIdLst[i].rId
        prs.part.drop_rel(rId)
        del prs.slides._sldIdLst[i]

    # Slide Layouts
    title_slide_layout = prs.slide_layouts[0]
    content_slide_layout = prs.slide_layouts[1]
    chart_slide_layout = prs.slide_layouts[2]
    bubble_slide_layout = prs.slide_layouts[3]
    closing_slide_layout = prs.slide_layouts[4]

    # Set width and height of presentation
    prs.slide_width = Inches(mdl_p['b_figsize'][0])
    prs.slide_height = Inches(mdl_p['b_figsize'][1])

    # Add title slide
    slide = prs.slides.add_slide(title_slide_layout)
    title = slide.shapes.title
    title.text = instance.data_name + " Classification Results (" + instance.solution['name'] + ")"
    # print(len(slide.placeholders))
    # print(slide.placeholders)
    # content = slide.placeholders[1]
    # content.text = "Rank, Name\nAFPC/DSYA\nTBD"

    # Initial content slide
    slide = prs.slides.add_slide(content_slide_layout)
    title = slide.shapes.title
    title.text = "Overview"
    content = slide.placeholders[1]
    content.text = "Here's where the overview goes"

    # Size of the chart
    # top, left = Inches(instance.mdl_p['ch_top']), Inches(instance.mdl_p['ch_left'])
    # height, width = Inches(instance.mdl_p['ch_height']), Inches(instance.mdl_p['ch_width'])

    # # Size of the chart
    # top, left = Inches(0), Inches(0)
    # height, width = Inches(mdl_p['figsize'][1]), Inches(mdl_p['figsize'][0])

    # Get the file paths to all the relevant images
    folder_path = instance.export_paths['Analysis & Results'] + instance.solution_name + "/"
    folder = os.listdir(folder_path)
    chart_paths = {}
    for file in folder:
        if instance.solution_name in file and '.png' in file:
            chart_paths[file] = folder_path + file

    # Loop through the pictures I want in the order I want
    chart_text_order = {"PGL": ["Combined Quota", "quantity_bar"], "BUBBLE CHART": ["Cadet Choice.png"],
                        "SOC by Accessions Group": ["Accessions", "SOC Chart"],
                        "Gender by Accessions Group": ["Accessions", "Gender Chart"],
                        "Race by Accessions Group": ["Accessions", "Race Chart"],
                        "Ethnicity by Accessions Group": ["Accessions", "Ethnicity Chart"],
                        "SOC by AFSC": ["Extra Measure", "SOC Chart_proportion"],
                        "Gender by AFSC": ["Extra Measure", "Gender Chart_proportion"],
                        "Race by AFSC": ["Extra Measure", "Race Chart_proportion"],
                        "Ethnicity by AFSC": ["Extra Measure", "Ethnicity Chart_proportion"],
                        "Cadet Preference by AFSC": ["Utility", "quantity_bar_choice"],
                        "AFSC Preference": ["Norm Score", "quantity_bar_choice"],
                        "62EXE BUBBLE CHART": ["62EXE Specific Choice.png"],}
    for title_text in chart_text_order:

        # Loop through each potential image
        for file in chart_paths:

            # Determine if this is the image I want here
            found = True
            for test_text in chart_text_order[title_text]:
                if test_text not in file:
                    found = False
                    break

            # If this is the file I want, we do things
            if found:

                # Determine the layout of the chart needed
                if "BUBBLE CHART" in title_text:

                    # Create the bubble chart slide
                    slide = prs.slides.add_slide(bubble_slide_layout)

                    # Add the image to the slide
                    for shape in slide.placeholders:
                        if "Picture" in shape.name:
                            shape.insert_picture(chart_paths[file])
                else:

                    # Create the AFSC/Accessions Chart slide
                    slide = prs.slides.add_slide(chart_slide_layout)
                    title = slide.shapes.title
                    title.text = title_text

                    # # Add the picture to the slide
                    # slide.shapes.add_picture(chart_paths[file], left, top, height=height, width=width)

                    # Add the image to the slide
                    for shape in slide.placeholders:
                        if "Picture" in shape.name:
                            shape.insert_picture(chart_paths[file])

                # Break out of this loop since we found the image we want
                break


    # Add closing slide
    prs.slides.add_slide(closing_slide_layout)

    # Save the PowerPoint
    filepath = instance.export_paths['Analysis & Results'] + instance.solution_name + '/' + \
               instance.data_name + ' ' + instance.solution_name + '.pptx'
    prs.save(filepath)

generate_comparison_slides(instance)

Function to generate the results slides for a particular problem instance with solution

Source code in afccp/visualizations/slides.py
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
def generate_comparison_slides(instance):
    """
    Function to generate the results slides for a particular problem instance with solution
    """

    # Shorthand
    mdl_p = instance.mdl_p

    # Build the presentation object
    prs = Presentation()
    blank_slide_layout = prs.slide_layouts[6]

    # Set width and height of presentation
    prs.slide_width = Inches(mdl_p['b_figsize'][0])
    prs.slide_height = Inches(mdl_p['b_figsize'][1])

    # Size of the chart
    top, left = Inches(0), Inches(0)

    # Get the file paths to all the relevant images
    folder_path = instance.export_paths['Analysis & Results'] + "Comparison Charts/"
    folder = os.listdir(folder_path)

    # Sort the files in the folder according to my preferred method
    chart_paths = {}
    keyword_order = [['Combined Quota'], ['Tier 1'], ['Male'], ['USAFA Proportion'], ['Merit'], ['Norm Score'],
                     ['Utility', 'dot'], ['Utility', 'mean_preference'], ['Utility', 'median_preference'],
                     ['Utility', 'Histogram'], ['Pareto', 'Cadets.png'], ['Pareto', ').png'], ['Similarity']]

    # Loop through each set of "keywords"
    for keywords in keyword_order:

        # Loop through each file until we have a match
        for file in folder:

            # Loop through all keywords to see if we have a match
            match = True
            for keyword in keywords:
                if keyword not in file:
                    match = False
                    break

            # If we have a "match", add it!
            if match:
                chart_paths[file] = folder_path + file
                folder.remove(file)  # Remove this file since we've accounted for it
                break

    # Add any remaining files
    for file in folder:
        if '.png' in file:
            chart_paths[file] = folder_path + file

    # Loop through each image file path to add the image to the presentation
    for file in chart_paths:

        # Add an empty slide
        slide = prs.slides.add_slide(blank_slide_layout)

        # Add the picture to the slide
        slide.shapes.add_picture(chart_paths[file], left, top)  #, height=height, width=width)

    # Save the PowerPoint
    filepath = instance.export_paths['Analysis & Results'] + instance.data_name + ' ' + 'Comparison.pptx'
    prs.save(filepath)

create_animation_slides(instance)

Function to generate the results slides for a particular problem instance with solution

Source code in afccp/visualizations/slides.py
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
def create_animation_slides(instance):
    """
    Function to generate the results slides for a particular problem instance with solution
    """

    # Shorthand
    mdl_p, sequence = instance.mdl_p, instance.solution['iterations']['sequence']

    # Build the presentation object
    prs = Presentation()
    blank_slide_layout = prs.slide_layouts[6]

    # Set width and height of presentation
    prs.slide_width = Inches(mdl_p['b_figsize'][0])
    prs.slide_height = Inches(mdl_p['b_figsize'][1])

    # Size of the chart
    top, left = Inches(0), Inches(0)
    height, width = Inches(mdl_p['b_figsize'][1]), Inches(mdl_p['b_figsize'][0])

    # Make sure we have the "sequence" folder
    if sequence not in os.listdir(instance.export_paths['Analysis & Results'] + "Cadet Board/"):
        raise ValueError("Error. Sequence folder '" + sequence + "' not in 'Cadet Board' folder.")

    # Get the file path to the sequence folder
    sequence_folder_path = instance.export_paths['Analysis & Results'] + "Cadet Board/" + sequence + '/'

    # Make sure we have the "focus" folder
    if mdl_p['focus'] not in os.listdir(sequence_folder_path):
        raise ValueError("Error. Focus folder '" + mdl_p['focus'] + "' not in '" + sequence + "' sequence folder.")

    # Files in the focus folder
    focus_folder_path = sequence_folder_path + mdl_p['focus'] + '/'
    folder = np.array(os.listdir(focus_folder_path))

    # Regular solution iterations frames: 1.png for example
    if "1.png" in folder:

        # Sort the folder in order by the frames
        int_vals = np.array([int(file[0]) for file in folder])
        indices = np.argsort(int_vals)
        img_paths = {file: focus_folder_path + file for file in folder[indices]}

    # Matching Algorithm proposals/rejections frames: 1 (Proposals).png & 1 (Rejections).png for example
    else:

        # The integer values at the beginning of each file
        int_vals = np.array([int(file[:2]) for file in folder])
        min, max = np.min(int_vals), np.max(int_vals)

        # Loop through the files to get the ordered list of frames
        img_paths = {}
        for val in np.arange(min, max + 1):
            indices = np.where(int_vals == val)[0]
            files = folder[indices]
            if len(files) > 1:
                if 'Proposals' in files[0]:
                    img_paths[files[0]] = focus_folder_path + files[0]
                    img_paths[files[1]] = focus_folder_path + files[1]
                else:
                    img_paths[files[1]] = focus_folder_path + files[1]
                    img_paths[files[0]] = focus_folder_path + files[0]
            else:
                img_paths[files[0]] = focus_folder_path + files[0]

    # Loop through each image file path to add the image to the presentation
    for file in img_paths:

        # Add an empty slide
        slide = prs.slides.add_slide(blank_slide_layout)

        # Add the picture to the slide
        slide.shapes.add_picture(img_paths[file], left, top, height=height, width=width)

    # Save the PowerPoint
    filepath = sequence_folder_path + mdl_p['focus'] + '.pptx'
    prs.save(filepath)

create_animated_presentation(instance, num_intro_slides=3)

Generates a PowerPoint presentation with: - Orientation slide (if available) - First few still frames - Animated GIF slide with remaining frames - Final solution slide (if available)

Parameters: instance: Object containing model data and paths num_intro_slides (int): Number of still slides to include before GIF

Source code in afccp/visualizations/slides.py
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
def create_animated_presentation(instance, num_intro_slides=3):
    """
    Generates a PowerPoint presentation with:
    - Orientation slide (if available)
    - First few still frames
    - Animated GIF slide with remaining frames
    - Final solution slide (if available)

    Parameters:
        instance: Object containing model data and paths
        num_intro_slides (int): Number of still slides to include before GIF
    """
    # Shorthand
    mdl_p = instance.mdl_p
    sequence = instance.solution['iterations']['sequence']

    # Build the presentation
    prs = Presentation()
    blank_slide_layout = prs.slide_layouts[6]
    prs.slide_width = Inches(mdl_p['b_figsize'][0])
    prs.slide_height = Inches(mdl_p['b_figsize'][1])
    top, left = Inches(0), Inches(0)
    height, width = Inches(mdl_p['b_figsize'][1]), Inches(mdl_p['b_figsize'][0])

    # Paths
    base_path = instance.export_paths['Analysis & Results'] + "Cadet Board/"
    sequence_path = os.path.join(base_path, sequence)
    focus_path = os.path.join(sequence_path, mdl_p['focus'])

    # Get sorted image files
    folder = np.array(os.listdir(focus_path))
    image_files = sorted([f for f in folder if f.endswith('.png')],
                         key=lambda x: int(''.join(filter(str.isdigit, x.split()[0]))))

    # --- Orientation Slide ---
    orientation_path = os.path.join(focus_path, 'orientation.png')
    if os.path.exists(orientation_path):
        slide = prs.slides.add_slide(blank_slide_layout)
        slide.shapes.add_picture(orientation_path, left, top, height=height, width=width)

    # --- Still Slides ---
    for i, file in enumerate(image_files[:num_intro_slides]):
        slide = prs.slides.add_slide(blank_slide_layout)
        slide.shapes.add_picture(os.path.join(focus_path, file), left, top, height=height, width=width)

    # --- Create GIF from remaining images ---
    gif_images = []
    for file in image_files[num_intro_slides:]:
        img = Image.open(os.path.join(focus_path, file)).convert("RGB")
        gif_images.append(img)

    if gif_images:
        gif_path = os.path.join(focus_path, 'animation.gif')
        gif_images[0].save(
            gif_path,
            save_all=True,
            append_images=gif_images[1:],
            duration=500,
            loop=0
        )

        # Add GIF slide (note: PowerPoint may require manual replacement depending on viewer support)
        slide = prs.slides.add_slide(blank_slide_layout)
        slide.shapes.add_picture(gif_path, left, top, height=height, width=width)

    # --- Final Image (if exists) ---
    file = image_files[len(image_files) - 1]
    slide = prs.slides.add_slide(blank_slide_layout)
    slide.shapes.add_picture(os.path.join(focus_path, file), left, top, height=height, width=width)

    # Save presentation
    output_path = os.path.join(sequence_path, mdl_p['focus'] + '_animated.pptx')
    prs.save(output_path)