| <!DOCTYPE html> |
| <html lang="fr"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Généalogie des Modèles</title> |
| |
| |
| <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> |
| |
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css"> |
| |
| |
| <link rel="stylesheet" href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" /> |
| <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}"> |
|
|
| <style> |
| body { |
| font-size: 1.05rem; |
| } |
| .legend-color { |
| width: 1em; |
| height: 1em; |
| display: inline-block; |
| border-radius: 2px; |
| } |
| .legend-color.edge { |
| border-radius: 0; |
| height: 0.25em; |
| transform: translateY(-0.35em); |
| } |
| </style> |
| </head> |
|
|
| <body class="d-flex flex-column min-vh-100"> |
| <header class="navbar navbar-expand-lg navbar-light bg-light border-bottom"> |
| <div class="container"> |
| <a class="navbar-brand" href="/"> |
| <span class="fw-bold">Généalogie des Modèles </span> |
| <small class="d-block text-muted">Exploration des relations entre modèles et datasets (base de données actualisée le 01/09/2025)</small> |
| </a> |
| </div> |
| </header> |
|
|
| <main class="container mt-4 flex-grow-1"> |
| |
| <div class="row"> |
| <div class="col-12"> |
| <h1 class="display-5">Recherche dans la base de données HuggingFace</h1> |
| <p class="lead">Explorez la généalogie des modèles</p> |
| </div> |
| </div> |
| |
| <div class="row g-3 mb-4"> |
| <div class="col-12"> |
| <form method="POST" action="{{ url_for('findnode') }}" autocomplete="off"> |
| <input type="hidden" name="filter" value="{{ search.filter or '' }}"> |
| |
| <div class="row g-3 align-items-end"> |
| |
| <div class="col-12 col-md-5"> |
| <label for="search-input" class="form-label fw-bold">Nom à rechercher</label> |
| <div class="position-relative"> |
| <input type="text" name="name" id="search-input" class="form-control" |
| placeholder="Taper le nom du modèle ou l'id de son repo Hugging Face." |
| value="{{ request.form.name or '' }}" required autocomplete="off" /> |
| <ul id="suggestions-list" class="list-group position-absolute w-100" style="display: none; z-index: 1000;"></ul> |
| </div> |
| </div> |
| |
| |
| <div class="col-12 col-md-3"> |
| <label class="form-label fw-bold">Filtres</label> |
| <div class="d-flex gap-3"> |
| <div class="form-check"> |
| <input class="form-check-input" type="checkbox" name="filters" value="Model" id="filter-model" |
| {% if 'Model' in search.filters %}checked{% endif %}> |
| <label class="form-check-label" for="filter-model">Modèle</label> |
| </div> |
| <div class="form-check"> |
| <input class="form-check-input" type="checkbox" name="filters" value="Dataset" id="filter-dataset" |
| {% if 'Dataset' in search.filters %}checked{% endif %}> |
| <label class="form-check-label" for="filter-dataset">Dataset</label> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="col-12 col-md-4"> |
| <label class="form-label fw-bold">Profondeur de recherche</label> |
| <div class="d-flex align-items-center gap-3"> |
| <div class="form-check form-switch"> |
| <input class="form-check-input" type="checkbox" id="depth-unlimited" name="depth_unlimited" checked> |
| <label class="form-check-label" for="depth-unlimited">Illimitée</label> |
| </div> |
| <div class="input-group input-group-sm"> |
| <span class="input-group-text">Limité à:</span> |
| <input class="form-control" type="number" name="depth" id="depth" |
| value="{{ request.form.depth }}" min="1" max="5" disabled> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="row mt-4"> |
| <div class="col-12"> |
| <button type="submit" name="submit" value="find_node" class="btn btn-primary w-100"> |
| <i class="bi bi-search me-1"></i>Rechercher (le chargement peut prendre jusqu'à une minute pour les grandes généalogies) |
| </button> |
| </div> |
| </div> |
| </form> |
| </div> |
| </div> |
| |
| |
| {% if message %} |
| <div class="alert alert-info mt-3"><p class="mb-0">{{ message }}</p></div> |
| {% endif %} |
|
|
| |
| {% if highlights and graph_data.nodes %} |
| <div id="highlights-view" class="mt-5 bg-light border rounded-3 p-4"> |
| {% set searched_node = (graph_data.nodes | selectattr('id', 'equalto', search.name) | list | first) or {} %} |
| <div class="row justify-content-center align-items-stretch g-4"> |
| |
| <div class="col-12 col-lg-3"> |
| <div class="d-flex justify-content-center gap-2 mb-3"> |
| <h4 class="h5 text-center mb-3">Modèles importants de l'ascendance |
| |
| <i class="bi bi-info-circle-fill text-secondary align-middle ms-2" |
| data-bs-toggle="tooltip" |
| data-bs-placement="right" |
| title="Les modèles qui précèdent le modèle recherché dans la généalogie, c’est-à-dire, les modèles à partir desquels le modèle recherché a été constitué"> |
| </i> |
| </h4> |
| </div> |
| <div class="card"> |
| <div class="card-body p-2"> |
| {% if not highlights.asc_unique_models %} |
| <p class="text-muted small m-2">Aucun modèle parent trouvé.</p> |
| {% else %} |
| {% for model in highlights.asc_unique_models %} |
| <div class="card mb-2 position-relative "> |
| |
| <div class="card-body p-2"> |
| |
| <div class="d-flex flex-wrap justify-content-center gap-1 mb-2"> |
| {% for badge in model.badges %} |
| <span class="badge {{ badge.class }} on-top" |
| data-bs-toggle="tooltip" |
| data-bs-placement="top" |
| title="{{ badge.title }}"> |
| {{ badge.text }} |
| </span> |
| {% endfor %} |
| </div> |
| |
| <div class="text-center"> |
| |
| <h6 class="card-title small mb-1 text-break card-interactive"> |
| <a href="https://huggingface.co/{{ model.name }}" target="_blank" rel="noopener noreferrer" class="stretched-link" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-html="true" |
| title="<b>Informations :</b> |
| <br>Auteur : {{ (model.name.split('/') | first) if '/' in model.name else 'Inconnu' }}<br> |
| Ciations : {{ '{:,.0f}'.format((model.citation_count or 0) | int).replace(',', ' ') }}<br> |
| J'aime : {{ model.likes or 'Inconnu' }}<br> |
| Date publication : {{ model.createdAt or 'N/A' }}<br> |
| Tâche : {{ model.task or 'Inconnue' }}<br> |
| License : {{ model.license or 'Inconnue' }}" |
| > |
| {{ model.name }} |
| </a> |
| </h6> |
| <p class="card-text" style="font-size: 0.75em; line-height: 1.2;"> |
| {{ "{:,.0f}".format((model.downloads or 0) | int).replace(',', ' ') }} téléchargements<br> |
| {{ "{:,.0f}".format((model.citation_count or 0) | int).replace(',', ' ') }} citations |
| </p> |
| </div> |
| </div> |
| </div> |
| {% endfor %} |
| {% endif %} |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="col-lg-1 d-none d-lg-flex justify-content-center"> |
| <i class="bi bi-arrow-right fs-1 text-secondary"></i> |
| </div> |
|
|
| |
| <div class="col-12 col-lg-4 " id="center-column"> |
| <h3 class="h5 text-center mb-3">Modèle recherché</h3> |
| <a href="https://huggingface.co/{{ searched_node.id }}" target="_blank" rel="noopener noreferrer" class="text-white"> |
| <div class="card card-recherche text-center border-2 "> |
| <div class="card-header card-recherche-header text-white"> |
| <h5 class="card-title h6 mb-0 "> |
| {{ searched_node.id }} |
| </h5> |
| </div> |
| <div class="card-body p-3"> |
| <div class="row"> |
| <div class="col"> |
| <p class="fw-bold mb-0">{{ "{:,.0f}".format((searched_node.ascendantsCount or 0) | int).replace(',', ' ') }}</p> |
| <p class="small text-muted">ascendant(s) |
| |
| <i class="bi bi-info-circle-fill ms-2" |
| data-bs-toggle="tooltip" |
| data-bs-placement="right" |
| title="Nombre de modèles qui précèdent le modèle recherché dans la généalogie"> |
| </i> |
| </p> |
| |
| </div> |
| <div class="col"> |
| <p class="fw-bold mb-0">{{ "{:,.0f}".format((searched_node.descendantsCount or 0) | int).replace(',', ' ') }}</p> |
| <p class="small text-muted">descendant(s) |
| <i class="bi bi-info-circle-fill ms-2" |
| data-bs-toggle="tooltip" |
| data-bs-placement="right" |
| title="Nombre de modèles directement ou indirectement constitués à partir du modèle recherché"> |
| </i> |
| </p> |
| </div> |
| </div> |
| <div class="row mt-2"> |
| <div class="col"><p class="fw-bold mb-0">{{ "{:,.0f}".format((searched_node.downloads or 0) | int).replace(',', ' ') }}</p><p class="small text-muted">téléchargement(s)</p></div> |
| <div class="col"><p class="fw-bold mb-0">{{ "{:,.0f}".format((searched_node.citationCount or 0) | int).replace(',', ' ') }}</p> |
| <p class="small text-muted">citation(s) |
| <i class="bi bi-info-circle-fill ms-2" |
| data-bs-toggle="tooltip" |
| data-bs-placement="right" |
| title="Nombre de descendants directs du modèle recherché : les enfants du modèle recherché"> |
| </i> |
| </p></div> |
| <div class="col"><p class="fw-bold mb-0">{{ "{:,.0f}".format((searched_node.likes or 0) | int).replace(',', ' ') }}</p><p class="small text-muted">J'aime</p></div> |
| </div> |
| <div class="mt-2"> |
| {% if searched_node.createdAt %}<span class="badge bg-light text-dark">Publié le: {{ searched_node.createdAt }}</span>{% endif %} |
| {% if searched_node.task %}<span class="badge bg-light text-dark">Tâche: {{ searched_node.task }}</span>{% endif %} |
| </div> |
| <hr> |
| </a> |
| <button id="show-graph-btn" class="btn btn-secondary"><i class="bi bi-diagram-3 me-1"></i>Voir l'arbre généalogique</button> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="col-lg-1 d-none d-lg-flex justify-content-center"> |
| <i class="bi bi-arrow-right fs-1 text-secondary"></i> |
| </div> |
|
|
| |
| <div class="col-12 col-lg-3"> |
| <div class="d-flex justify-content-center gap-2 mb-3"> |
| <h4 class="h5 text-center mb-3">Modèles importants de la descendance |
| |
| <i class="bi bi-info-circle-fill text-secondary align-middle ms-2" |
| data-bs-toggle="tooltip" |
| data-bs-placement="right" |
| title="Les modèles directement ou indirectement constitués à partir du modèle recherché"> |
| </i> |
| </h4> |
| </div> |
| <div class="card"> |
| <div class="card-body p-2"> |
| {% if highlights.desc_unique_models %} |
| {% for model in highlights.desc_unique_models %} |
| <div class="card mb-2 position-relative "> |
| <div class="card-body p-2"> |
| |
| <div class="d-flex flex-wrap justify-content-center gap-1 mb-2"> |
| {% for badge in model.badges %} |
| <span class="badge {{ badge.class }} on-top" |
| data-bs-toggle="tooltip" |
| data-bs-placement="top" |
| title="{{ badge.title }}"> |
| {{ badge.text }} |
| </span> |
| {% endfor %} |
| </div> |
| |
| <div class="text-center card-interactive"> |
| |
| <h6 class="card-title small mb-1 text-break"> |
| <a href="https://huggingface.co/{{ model.name }}" |
| target="_blank" |
| rel="noopener noreferrer" |
| class="stretched-link" |
| data-bs-toggle="tooltip" |
| data-bs-placement="top" |
| data-bs-html="true" |
| title="<b>Informations :</b><br> |
| Auteur : {{ (model.name.split('/') | first) if '/' in model.name else 'Inconnu' }}<br> |
| Téléchargements : {{ '{:,.0f}'.format((model.downloads or 0) | int).replace(',', ' ') }}<br> |
| J'aime : {{ model.likes or 'Inconnu' }}<br> |
| Date publication : {{ model.createdAt or 'N/A' }}<br> |
| Tâche : {{ model.task or 'Inconnue' }}<br> |
| License : {{ model.license or 'Inconnue' }}"> |
| {{ model.name }} |
| </a> |
| </h6> |
| <p class="card-text" style="font-size: 0.75em; line-height: 1.2;"> |
| {{ "{:,.0f}".format((model.downloads or 0) | int).replace(',', ' ') }} téléchargements<br> |
| {{ "{:,.0f}".format((model.citation_count or 0) | int).replace(',', ' ') }} citations |
| </p> |
| </div> |
| </div> |
| </div> |
| {% endfor %} |
| {% else %} |
| <p class="text-muted small m-2">Aucun modèle dérivé trouvé.</p> |
| {% endif %} |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| |
| <div class="row mt-5 mb-5"> |
| <hr class="mb-5"> |
| <div class="col-12"> |
| <div class="mb-5"> |
| <h4 class="h4">Descendance détaillée</h4> |
| <div class="table-responsive"> |
| <table id="descendance-table" class="table table-bordered table-striped table-hover"> |
| <thead> |
| <tr> |
| <th scope="col">Modèle</th> |
| <th scope="col">Auteur</th> |
| <th scope="col">Téléchargements</th> |
| <th scope="col">Tâche</th> |
| <th scope="col">J'aime</th> |
| <th scope="col">Date de publication</th> |
| <th scope="col">Dataset utilisé</th> |
| <th scope="col">Licence</th> |
| <th scope="col">Distance au modèle recherché</th> |
| <th scope="col">Ascendants</th> |
| <th scope="col">Descendants</th> |
| <th scope="col">Citations</th> |
| </tr> |
| </thead> |
| </table> |
| </div> |
| </div> |
| <hr class="my-5"> |
| <div> |
| <h4 class="h4">Ascendance détaillée</h4> |
| <div class="table-responsive"> |
| <table id="ascendance-table" class="table table-bordered table-striped table-hover"> |
| <thead> |
| <tr> |
| <th scope="col">Modèle</th> |
| <th scope="col">Auteur</th> |
| <th scope="col">Téléchargements</th> |
| <th scope="col">Tâche</th> |
| <th scope="col">J'aime</th> |
| <th scope="col">Date de publication</th> |
| <th scope="col">Dataset utilisé</th> |
| <th scope="col">Licence</th> |
| <th scope="col">Distance au modèle recherché</th> |
| <th scope="col">Ascendants</th> |
| <th scope="col">Descendants</th> |
| <th scope="col">Citations</th> |
| </tr> |
| </thead> |
| </table> |
| </div> |
| </div> |
| </div> |
| <hr class="mt-5"> |
| </div> |
| <div id="genealogy-graph-section" style="display: none;" class="mt-5"> |
| <div class="row g-4"> |
| <div class="col-12 col-lg-8"> |
| <div class="card"> |
| <div class="card-header"><h5 class="card-title mb-0">Visualisation de l'arbre généalogique</h5></div> |
| <div class="card-body"> |
| <div id="sigma-container" data-graph='{{ graph_data | tojson | safe }}'></div> |
| </div> |
| </div> |
| <div id="node-info-card" class="card mt-3 card-interactive" style="display: none;"> <div class="card-body"> |
| <h5 class="card-title">Informations du nœud sélectionné</h5> |
| <div id="node-details" class="mt-2"></div> |
| </div> |
| </div> |
| </div> |
| <div class="col-12 col-lg-4"> |
| <div class="card mb-3"> |
| <div class="card-header"><h5 class="card-title mb-0">Légende - Nœuds</h5></div> |
| <div class="card-body"> |
| <ul id="legend-nodes" class="list-group list-group-flush"> |
| <li class="list-group-item d-flex align-items-center"><span class="legend-color me-2" style="background-color: #007bff;"></span>Personne (taille = nombre d'abonnés)</li> |
| <li class="list-group-item d-flex align-items-center"><span class="legend-color me-2" style="background-color: #092d53;"></span>Organisation (taille = nombre d'abonnés)</li> |
| <li class="list-group-item d-flex align-items-center"><span class="legend-color me-2" style="background-color:#7D7D7D;"></span>Modèle (taille = nombre de téléchargements)</li> |
| </ul> |
| </div> |
| </div> |
| <div class="card"> |
| <div class="card-header"><h5 class="card-title mb-0">Légende - Relations</h5></div> |
| <div class="card-body"> |
| <ul id="legend-edges" class="list-group list-group-flush"></ul> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| {% endif %} |
| </main> |
|
|
| |
| <footer class="bg-dark text-white text-center p-4 mt-auto"> |
| <div class="container"> |
| <p class="mb-0">Application de recherche et visualisation des relations entre modèles et jeux de données publiés sur la plateforme HuggingFace. © 2025</p> |
| </div> |
| </footer> |
|
|
| <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> |
| <script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script> |
| <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/sigma.js/2.4.0/sigma.min.js"></script> |
| <script src="https://unpkg.com/graphology@0.25.1/dist/graphology.umd.min.js"></script> |
| <script src="https://cdn.jsdelivr.net/npm/graphology-library/dist/graphology-library.min.js"></script> |
| <script src="{{ url_for('static', filename='js/utils.js') }}"></script> |
| <script src="{{ url_for('static', filename='js/script.js') }}"></script> |
| <script> |
| document.addEventListener('DOMContentLoaded', function () { |
| const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); |
| const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)); |
| }); |
| </script> |
| </body> |
| </html> |