From 5cf2bc932bc8885ea5b08dccce99a0b0cd772c5c Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 5 Jan 2026 14:41:49 -0500 Subject: [PATCH 1/8] Add file icons to FileList module --- signac_dashboard/modules/file_list.py | 38 +++++++++++++++++++ .../templates/cards/file_list.html | 5 ++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/signac_dashboard/modules/file_list.py b/signac_dashboard/modules/file_list.py index df2fc202..404d43cb 100644 --- a/signac_dashboard/modules/file_list.py +++ b/signac_dashboard/modules/file_list.py @@ -1,6 +1,7 @@ # Copyright (c) 2022 The Regents of the University of Michigan # All rights reserved. # This software is licensed under the BSD 3-Clause License. +import mimetypes import os from flask import render_template @@ -36,6 +37,42 @@ def __init__( ) self.prefix_jobid = prefix_jobid + def _get_icon(self, filename): + _, ext = os.path.splitext(filename) + ext = ext.lstrip(".").lower() + + icon_map = { + "pdf": "fa-file-pdf", + "zip": "fa-file-archive", + "tar": "fa-file-archive", + "gz": "fa-file-archive", + "7z": "fa-file-archive", + } + if ext in icon_map: + return icon_map[ext] + + mtype, _ = mimetypes.guess_type(filename) + print(mtype) + if mtype: + if mtype.startswith("image/"): + return "fa-file-image" + if mtype.startswith("audio/"): + return "fa-file-audio" + if mtype.startswith("video/"): + return "fa-file-video" + if "word" in mtype: + return "fa-file-word" + if "excel" in mtype or "spreadsheet" in mtype or "csv" in mtype: + return "fa-file-excel" + if "powerpoint" in mtype or "presentation" in mtype: + return "fa-file-powerpoint" + if "x-" in mtype or "json" in mtype: + return "fa-file-code" + if mtype.startswith("text/"): + return "fa-file-alt" + + return "fa-file" + def download_name(self, job, filename): if self.prefix_jobid: return f"{str(job)}_{filename}" @@ -49,6 +86,7 @@ def get_cards(self, job): "name": filename, "jobid": job._id, "download": self.download_name(job, filename), + "icon": self._get_icon(filename), } for filename in os.listdir(job.path) ), diff --git a/signac_dashboard/templates/cards/file_list.html b/signac_dashboard/templates/cards/file_list.html index c7234556..3fefacc7 100644 --- a/signac_dashboard/templates/cards/file_list.html +++ b/signac_dashboard/templates/cards/file_list.html @@ -1,5 +1,8 @@ From 15f1220a37dd1f20720403b7ef07708b13314cf8 Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 5 Jan 2026 14:48:06 -0500 Subject: [PATCH 2/8] Update changelog.txt --- changelog.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changelog.txt b/changelog.txt index 421b2417..9d965c8a 100644 --- a/changelog.txt +++ b/changelog.txt @@ -10,6 +10,11 @@ Version 0.7 [0.7.0] -- 2025-xx-xx --------------------- +Added ++++++ + +- File icons for the FileList module (#287). + Updated +++++++ From c81361278c24b0c6952c50501e30bcce875b7cc3 Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 5 Jan 2026 14:54:21 -0500 Subject: [PATCH 3/8] Remove debug print --- signac_dashboard/modules/file_list.py | 1 - 1 file changed, 1 deletion(-) diff --git a/signac_dashboard/modules/file_list.py b/signac_dashboard/modules/file_list.py index 404d43cb..9dfba028 100644 --- a/signac_dashboard/modules/file_list.py +++ b/signac_dashboard/modules/file_list.py @@ -52,7 +52,6 @@ def _get_icon(self, filename): return icon_map[ext] mtype, _ = mimetypes.guess_type(filename) - print(mtype) if mtype: if mtype.startswith("image/"): return "fa-file-image" From 820c31ca35ec57937e77cdf02d313a3febad4165 Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 5 Jan 2026 14:59:47 -0500 Subject: [PATCH 4/8] Add test --- tests/test_dashboard.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/test_dashboard.py b/tests/test_dashboard.py index 291e459c..caebab98 100644 --- a/tests/test_dashboard.py +++ b/tests/test_dashboard.py @@ -8,6 +8,7 @@ import unittest from urllib.parse import quote as urlquote +import pytest from signac import init_project import signac_dashboard.modules @@ -207,5 +208,28 @@ def test_navigator_module(self): assert "disabled>min" in response # no previous job for b +@pytest.mark.parametrize( + "filename,expected", + [ + ("test.pdf", "fa-file-pdf"), + ("archive.zip", "fa-file-archive"), + ("image.png", "fa-file-image"), + ("audio.mp3", "fa-file-audio"), + ("video.mp4", "fa-file-video"), + ("text.txt", "fa-file-alt"), + ("text.csv", "fa-file-excel"), + ("code.sh", "fa-file-code"), + ("code.py", "fa-file-code"), + ("code.h", "fa-file-code"), + ("code.c", "fa-file-code"), + ("code.json", "fa-file-code"), + ], +) +def test_file_list_icon(filename, expected): + """Test that FileList._get_icon returns correct icon classes.""" + file_list = signac_dashboard.modules.FileList() + assert file_list._get_icon(filename) == expected + + if __name__ == "__main__": unittest.main() From d652012a9df2b51df44e21ba9cba7410745260d5 Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 5 Jan 2026 15:27:56 -0500 Subject: [PATCH 5/8] Fix code types on windows --- signac_dashboard/modules/file_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/signac_dashboard/modules/file_list.py b/signac_dashboard/modules/file_list.py index 9dfba028..34d17a55 100644 --- a/signac_dashboard/modules/file_list.py +++ b/signac_dashboard/modules/file_list.py @@ -65,7 +65,7 @@ def _get_icon(self, filename): return "fa-file-excel" if "powerpoint" in mtype or "presentation" in mtype: return "fa-file-powerpoint" - if "x-" in mtype or "json" in mtype: + if "x-" in mtype or mtype.endswith(("json", "cpp", "c", "h", "rs")): return "fa-file-code" if mtype.startswith("text/"): return "fa-file-alt" From 3adbbda64240d4ef13a9556bce59337f82590ec4 Mon Sep 17 00:00:00 2001 From: janbridley Date: Mon, 5 Jan 2026 15:32:58 -0500 Subject: [PATCH 6/8] Fix mimetypes on windows --- signac_dashboard/modules/file_list.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/signac_dashboard/modules/file_list.py b/signac_dashboard/modules/file_list.py index 34d17a55..2db3941c 100644 --- a/signac_dashboard/modules/file_list.py +++ b/signac_dashboard/modules/file_list.py @@ -8,6 +8,13 @@ from signac_dashboard.module import Module +# Register mimetypes for C/C++ files that are not present on Windows +mimetypes.add_type("text/x-c", ".c") +mimetypes.add_type("text/x-c", ".h") +mimetypes.add_type("text/x-c++", ".cpp") +mimetypes.add_type("text/x-c++", ".hpp") +mimetypes.add_type("text/x-c++", ".cc") + class FileList(Module): """Lists files in the job directory with download links. @@ -65,7 +72,7 @@ def _get_icon(self, filename): return "fa-file-excel" if "powerpoint" in mtype or "presentation" in mtype: return "fa-file-powerpoint" - if "x-" in mtype or mtype.endswith(("json", "cpp", "c", "h", "rs")): + if "x-" in mtype or "json" in mtype: return "fa-file-code" if mtype.startswith("text/"): return "fa-file-alt" From 076565084005b7651553a9e179f628d8900a1681 Mon Sep 17 00:00:00 2001 From: janbridley Date: Tue, 6 Jan 2026 11:16:58 -0500 Subject: [PATCH 7/8] Address comments --- signac_dashboard/modules/file_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/signac_dashboard/modules/file_list.py b/signac_dashboard/modules/file_list.py index 2db3941c..9c7c3711 100644 --- a/signac_dashboard/modules/file_list.py +++ b/signac_dashboard/modules/file_list.py @@ -72,7 +72,7 @@ def _get_icon(self, filename): return "fa-file-excel" if "powerpoint" in mtype or "presentation" in mtype: return "fa-file-powerpoint" - if "x-" in mtype or "json" in mtype: + if mtype.startswith("application/x-") or "json" in mtype: return "fa-file-code" if mtype.startswith("text/"): return "fa-file-alt" From 3b526b56d319b3761cbb9e48c732b31f584ebadc Mon Sep 17 00:00:00 2001 From: janbridley Date: Tue, 6 Jan 2026 11:17:26 -0500 Subject: [PATCH 8/8] Standardize types --- signac_dashboard/modules/file_list.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/signac_dashboard/modules/file_list.py b/signac_dashboard/modules/file_list.py index 9c7c3711..79e5028c 100644 --- a/signac_dashboard/modules/file_list.py +++ b/signac_dashboard/modules/file_list.py @@ -9,11 +9,11 @@ from signac_dashboard.module import Module # Register mimetypes for C/C++ files that are not present on Windows -mimetypes.add_type("text/x-c", ".c") -mimetypes.add_type("text/x-c", ".h") -mimetypes.add_type("text/x-c++", ".cpp") -mimetypes.add_type("text/x-c++", ".hpp") -mimetypes.add_type("text/x-c++", ".cc") +mimetypes.add_type("application/x-c", ".c") +mimetypes.add_type("application/x-c", ".h") +mimetypes.add_type("application/x-c++", ".cpp") +mimetypes.add_type("application/x-c++", ".hpp") +mimetypes.add_type("application/x-c++", ".cc") class FileList(Module):