Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
d4c7f96
Merge pull request #48 from jmhorcas/develop
Jun 13, 2023
dc8ba38
Merge pull request #49 from jmhorcas/develop
jmhorcas Jul 4, 2023
3374a49
Update README.md
jmhorcas Jul 4, 2023
e49a26a
Update README.md
jmhorcas Jul 4, 2023
3d54a1f
Update README.md
jmhorcas Feb 15, 2024
007d294
Update README.md
jmhorcas Feb 15, 2024
5e70c1f
feat: add feature_attributes metric to FMProperties
AngelaPGM Jun 11, 2024
1add47b
feat: add features_with_attributes metric to FMProperties. This commi…
AngelaPGM Jun 11, 2024
f8f2be0
feat: add ratio calculation to features_with_attributes metric
AngelaPGM Jun 11, 2024
e190b4c
chore: remove unnecessary blank lines
AngelaPGM Jun 11, 2024
a1938b6
feat: Add functionality to export SVG chart to PDF
AngelaPGM Jul 14, 2024
283baad
feat: change svg and png export to adjust size
AngelaPGM Jul 20, 2024
7d9bedf
feat: Enhance metric details with Bootstrap modal and improved UX
AngelaPGM Jul 20, 2024
3737313
feat: changed cursor to pointer in metrics
AngelaPGM Jul 20, 2024
8f0ee79
feat: Added tabs in the HTML to separate the existing logic from the …
AngelaPGM Jul 22, 2024
bc13507
feat: Implement Comprehensive ZIP File Processing for Feature Models
AngelaPGM Jul 24, 2024
14f81c7
feat: Add ratio average calculation to feature model metrics processing
AngelaPGM Jul 24, 2024
4051750
feat: added median, min and max
AngelaPGM Jul 24, 2024
37cf58a
feat: read title from metadata
AngelaPGM Jul 27, 2024
6608762
feat: unified datasetchart and simple chart js
AngelaPGM Jul 27, 2024
d123782
feat: ratio calculated with mean data
AngelaPGM Jul 27, 2024
803fc96
feat: Add drag-and-drop functionality for ZIP file uploads
AngelaPGM Jul 28, 2024
952c8e0
feat: Adjust CSS for sticky footer and content height
AngelaPGM Jul 28, 2024
f448135
feat(security): add Subresource Integrity (SRI) to jsPDF script
AngelaPGM Jul 28, 2024
44c9537
Update repository's structure
jmhorcas Jul 29, 2024
c95e9a2
fix: sonar issues
AngelaPGM Jul 31, 2024
f5c6b58
feat: add analysis mean and stats
AngelaPGM Jul 31, 2024
74479be
feat: add export functionality to dataset
AngelaPGM Aug 2, 2024
d2eb460
fix: sonar issues
AngelaPGM Aug 2, 2024
8786a9c
refactor: clean code
AngelaPGM Aug 6, 2024
0503657
feat: added threshold 'low, medium, high' from zip
AngelaPGM Aug 8, 2024
42e2ea5
feat: add landscape chart
AngelaPGM Aug 10, 2024
41e3c4f
feat; changed design + fix duplicated svg ids
AngelaPGM Aug 10, 2024
e1ecc7c
feat; add export button to landscape
AngelaPGM Aug 11, 2024
a4d3c9a
fix: fix white fill
AngelaPGM Aug 11, 2024
587bb65
fix: hide collapse icon when export
AngelaPGM Aug 11, 2024
13a8d13
feat: add landscape to dataset chart
AngelaPGM Aug 11, 2024
7339015
fix: fix bug
AngelaPGM Aug 11, 2024
4e5f521
feat: hide fields when null
AngelaPGM Aug 11, 2024
d86fcd7
test: add cypress and some tests e2e
AngelaPGM Sep 5, 2024
f9f0b94
fix: show error if threshold zip does not containd valid files
AngelaPGM Sep 5, 2024
c62f3c8
fix: fix bug that did not delete the temp file when the file was not …
AngelaPGM Sep 5, 2024
358c287
test: add more tests
AngelaPGM Sep 5, 2024
8271835
feat: add numpty to requirements.txt
AngelaPGM Sep 7, 2024
b678c42
fix: fix more sonar issues + scroll in the svg chart
AngelaPGM Sep 8, 2024
52f921e
refactor: change var to let
AngelaPGM Sep 8, 2024
da9cc61
Merge remote-tracking branch 'upstream/main'
AngelaPGM Sep 8, 2024
daa91d6
perf: reduce cognitive complexity
AngelaPGM Sep 8, 2024
93f550a
fix: revert bug in landscape view
AngelaPGM Sep 8, 2024
435905f
fix: fix export json and txt in dataset
AngelaPGM Sep 13, 2024
9d944ba
fix: add json and txt tests + fix functionality in dataset
AngelaPGM Sep 13, 2024
244c55b
fix: fix text plain export
AngelaPGM Sep 13, 2024
122a2bc
remove: remove unnecesary comment
AngelaPGM Sep 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,12 @@ dmypy.json

# Pyre type checker
.pyre/

# Cypress
cypress/screenshots/
cypress/videos/
cypress/downloads/
node_modules/
.DS_Store
Thumbs.db

35 changes: 32 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
# Table of Contents
- [Table of Contents](#table-of-contents)
- [FM Fact Label: A Configurable and Interactive Visualization of Feature Model Characterizations](#fm-fact-label-a-configurable-and-interactive-visualization-of-feature-model-characterizations)
- [Available online](#available-online)
- [Artifact description](#artifact-description)
- [How to use it](#how-to-use-it)
- [Deployment of the web application](#deployment-of-the-web-application)
- [Requirements](#requirements)
- [Download and install](#download-and-install)
- [Execution](#execution)
- [Video](#video)
- [Architecture and repository's structure and contents](#architecture-and-repositorys-structure-and-contents)
- [Software architecture](#software-architecture)
- [Repository's structure and contents](#repositorys-structure-and-contents)
- [References and third-party software](#references-and-third-party-software)

# FM Fact Label: A Configurable and Interactive Visualization of Feature Model Characterizations
A tool to generate visualizations of feature model characterizations as a fact label similar to the [nutritions fact label](https://en.wikipedia.org/wiki/Nutrition_facts_label).

## Available online
- [FM Fact Label](https://fmfactlabel.adabyron.uma.es/)


## Artifact description
*FM Fact Label* is an online web-based application that builds an FM characterization and generates its visualization as a fact label.

Expand All @@ -22,7 +31,7 @@ The fact label visualization is automatically generated using [D3](https://d3js.
## How to use it
The tool is currently deployed and available online in the following link:

https://web.diverso-lab.us.es/fmfactlabel
https://fmfactlabel.adabyron.uma.es/

The main use case of the tool is uploading an FM and automatically generates a visualization of its characterization which can be customized and exported. The use case can be described with the following steps:
- Upload an FM and provide metadata.
Expand All @@ -32,7 +41,7 @@ The main use case of the tool is uploading an FM and automatically generates a v
- Export the FM fact label and the FM characterization.

<video width="320" height="240" controls>
<source src="video_demo.mp4" type="video/mp4">
<source src="resources/video_demo.mp4" type="video/mp4">
</video>


Expand All @@ -41,6 +50,9 @@ The main use case of the tool is uploading an FM and automatically generates a v

### Requirements
- [Python 3.9+](https://www.python.org/)
- [Flask](https://flask.palletsprojects.com/en/3.0.x/)
- [Flama](https://www.flamapy.org/)
- [D3.js](https://d3js.org/)

### Download and install
1. Install [Python 3.9+](https://www.python.org/)
Expand Down Expand Up @@ -80,6 +92,23 @@ http://127.0.0.1:5000 or http://10.141.0.170:5000
https://user-images.githubusercontent.com/1789503/172726157-11ebe212-41f6-47a1-9ab7-ee378ed1aab7.mp4


## Architecture and repository's structure and contents
Here is a description of the architecture of the tool and the folders' structure and contents of this repository for those interesting in contributing to the project.

### Software architecture

![Software architecture](resources/architecture.png "Software architecture")

The tool offers a web service to upload the feature model and its metadata via an online form (`Web Service` component). It supports feature models in UVL and FeatureIDE formats. The `FM Characterization` module in the server-side gathers and manages all the feature model information. We distinguish three kinds of information: metadata (`FM Metadata`), structural metrics (`FM Metrics`), and analysis results (`FM Analysis`), treating all of them as an `FM property`. Each `FM Property` includes a name, a description, and a parent property for hierarchical organization in the fact label. Properties are associated with an `FM Property Measure` that provides the specific values of the property. For instance, the list of abstract features, their size, and ratio for the `ABSTRACT FEATURES` property. Analysis tasks are delegated to external tools, with the current implementation relying on [flama](https://www.flamapy.org/) (dark component).

### Repository's structure and contents
- [run.py](run.py): It is the entry point of the application that consists on a [Flask](https://flask.palletsprojects.com/en/3.0.x/) server to expose the tool's functionality.
- [fm_characterization](fm_characterization/): Contains the code related to the server-side of the architecture in charge of gathering all the information of the feature model that is needed to build the fact label. Concretely, it contains the `FM Characterization`, `FM Metadata`, `FM Metrics`, `FM Analysis`, `FM property`, and `FM Property Measure` modules, among other utils. The dependency with the [flama](https://www.flamapy.org/) library is on the `FM Analysis` module.
- [web](web/): Contains the code related with the client-side of the architecture in charge of building the visualization of the fact label from the JSON information provided by the server-side. Concretely, it contains the HTML, CSS, and JavaScript files, where the most important is the [fm_fact_label.js](web/js/fm_fact_label/fm_fact_label.js) script which contains the main code in [D3.js](https://d3js.org/) to build the visualization of the fact label. Also, the [fm_models](web/fm_models) contains the feature models examples availables in the tool.
- [resources](resources/): Contains the images and videos used in this README.md file.


## References and third-party software
- [D3: Data-Driven Documents](https://d3js.org/)
- [Python framework for automated analysis of feature models](https://github.com/diverso-lab/core)
- [Flama](https://www.flamapy.org/)
- [Flask](https://flask.palletsprojects.com/en/3.0.x/)
26 changes: 26 additions & 0 deletions cypress.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const { defineConfig } = require("cypress");
const fs = require('fs');
const path = require('path');

module.exports = defineConfig({
e2e: {
baseUrl: "http://127.0.0.1:5000/",
viewportWidth: 1920,
viewportHeight: 1080,
downloadsFolder: 'cypress/downloads',
setupNodeEvents(on, config) {
// Task to clear the downloads folder
on('task', {
clearDownloads() {
const downloadsFolder = config.downloadsFolder;
if (fs.existsSync(downloadsFolder)) {
fs.readdirSync(downloadsFolder).forEach(file => {
fs.unlinkSync(path.join(downloadsFolder, file));
});
}
return null;
}
});
},
},
});
263 changes: 263 additions & 0 deletions cypress/e2e/spec.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
import 'cypress-file-upload';


function fillFormAndSubmit({ name, description, author, year, reference, keywords, domain, fmFile, zipFile }) {
if (name) cy.get("#inputName").type(name);
if (description) cy.get("#inputDescription").type(description);
if (author) cy.get("#inputAuthor").type(author);
if (year) cy.get("#inputYear").type(year);
if (reference) cy.get("#inputReference").type(reference);
if (keywords) cy.get("#inputKeywords").type(keywords);
if (domain) cy.get("#inputDomain").type(domain);
if (fmFile) cy.get("#inputFM").attachFile(fmFile);
if (zipFile) cy.get("#inputZipThreshold").attachFile(zipFile);
cy.get("#submitButton").click();
}


function selectPizzasModelAndUploadZip() {
cy.visit("/");
cy.get("#inputExample").select("Pizzas");
cy.get("#inputExample").should("have.value", "Pizzas");
cy.get("#inputZipThreshold").attachFile("Modelos.zip");
cy.get("#submitButton").click();
}


function uploadDatasetAndProceed() {
cy.visit("/");
cy.get("#upload-zip-tab").click();
cy.get("#inputZip").attachFile("Modelos.zip");
cy.get("#submitZipButton").click();
}

describe("Analyze a Feature Model", () => {

before(() => {
cy.task('clearDownloads');
});

it("Uploads and verifies feature model analysis", () => {
cy.visit("/");
fillFormAndSubmit({
name: "Pizzas",
description: "This feature model is about pizzas!",
author: "Angela",
year: "2024",
reference: "https://fmfactlabel.adabyron.uma.es/",
keywords: "Pizza, Italian, Food",
domain: "Italian food",
fmFile: "Pizzas.uvl",
zipFile: "Modelos.zip"
});
cy.get('svg#FMFactLabelChart').should('exist');
});

it("Shows error when unsupported file type is uploaded", () => {
cy.visit("/");
fillFormAndSubmit({
name: "Pizzas",
description: "This feature model is about pizzas!",
author: "Angela",
year: "2024",
reference: "https://fmfactlabel.adabyron.uma.es/",
keywords: "Pizza, Italian, Food",
domain: "Italian food",
fmFile: "Texto.txt"
});
cy.get('.alert.alert-danger').should('exist')
.and('contain', 'Feature model format not supported.');
});

it("Shows error message when submitting empty form", () => {
cy.visit("/");
cy.get("#submitButton").click();
cy.get('.alert.alert-danger').should('exist')
.and('contain', 'Please upload a feature model or select one from the examples.');
cy.get('svg#FMFactLabelChart').should('not.exist');
});

it("Shows error when uploading ZIP without UVL files in Threshold", () => {
cy.visit("/");
fillFormAndSubmit({
name: "Pizzas",
description: "This feature model is about pizzas!",
author: "Angela",
year: "2024",
reference: "https://fmfactlabel.adabyron.uma.es/",
keywords: "Pizza, Italian, Food",
domain: "Italian food",
fmFile: "Pizzas.uvl",
zipFile: "Texto.zip"
});
cy.get('.alert.alert-danger').should('exist')
.and('contain', 'No valid UVL files found in the Threshold ZIP.');
});

it("Handles interactions with the feature model", () => {
selectPizzasModelAndUploadZip();
cy.get('svg#FMFactLabelChart').should('exist');
cy.get("#collapseSubProperties").click();
cy.get("#collapseSubProperties").click();
cy.get("#collapseZeroValues").click();
cy.get("#collapseZeroValues").click();
cy.get("#toggle-chart").click();
cy.get('svg#FMFactLabelChartLandscape').should('exist');
cy.get("#toggle-chart").click();
});

context("File Download Verification", () => {

beforeEach(() => {
cy.task('clearDownloads');
selectPizzasModelAndUploadZip();
});

it("Downloads SVG file", () => {
cy.get("#dropdownMenuLink").click();
cy.get("#saveSVG").click();
cy.readFile('cypress/downloads/Pizzas.svg').should('exist');
});

it("Downloads PNG file", () => {
cy.get("#dropdownMenuLink").click();
cy.get("#savePNG").click();
cy.readFile('cypress/downloads/Pizzas.png').should('exist');
});

it("Downloads PDF file", () => {
cy.get("#dropdownMenuLink").click();
cy.get("#savePDF").click();
cy.readFile('cypress/downloads/Pizzas.pdf').should('exist');
});

it("Exports model to JSON", () => {
cy.get("#dropdownMenuLink").click();
cy.get("#saveJSON").click();
cy.readFile('cypress/downloads/Pizzas.json').should('exist');
});

it("Exports model to TXT", () => {
cy.get("#dropdownMenuLink").click();
cy.get("#saveTXT").click();
cy.readFile('cypress/downloads/Pizzas.txt').should('exist');
});

it("Downloads Landscape SVG file", () => {
cy.get("#toggle-chart").click();
cy.get("#dropdownMenuLinkLandscape").click();
cy.get("#saveSVGLandscape").click();
cy.readFile('cypress/downloads/Pizzas.svg').should('exist');
});

it("Downloads Landscape PNG file", () => {
cy.get("#toggle-chart").click();
cy.get("#dropdownMenuLinkLandscape").click();
cy.get("#savePNGLandscape").click();
cy.readFile('cypress/downloads/Pizzas.png').should('exist');
});

it("Downloads Landscape PDF file", () => {
cy.get("#toggle-chart").click();
cy.get("#dropdownMenuLinkLandscape").click();
cy.get("#savePDFLandscape").click();
cy.readFile('cypress/downloads/Pizzas.pdf').should('exist');
});
});
});

describe("Analyze Feature Model Dataset", () => {

it("Uploads ZIP file successfully", () => {
uploadDatasetAndProceed();
cy.get('.alert.alert-danger').should('not.exist');
});

it("Shows error when ZIP without UVL files is uploaded", () => {
cy.visit("/");
cy.get("#upload-zip-tab").click();
cy.get("#inputZip").attachFile("Texto.zip");
cy.get("#submitZipButton").click();
cy.get('.alert.alert-danger').should('exist')
.and('contain', 'No valid UVL files found in the ZIP.');
});

it("Handles collapse/expand and chart interactions", () => {
uploadDatasetAndProceed();
cy.get('svg#FMFactLabelDataSetChart').should('exist');
cy.get("#collapseSubProperties").click();
cy.get("#collapseSubProperties").click();
cy.get("#collapseZeroValues").click();
cy.get("#collapseZeroValues").click();
cy.get("#toggle-chart-data-set").click();
cy.get('svg#FMFactLabelDataSetChartLandscape').should('exist');
cy.get("#toggle-chart-data-set").click();
});

context("File Download Verification", () => {

beforeEach(() => {
cy.task('clearDownloads');
});

it("Downloads SVG file", () => {
uploadDatasetAndProceed();
cy.get("#dropdownMenuLinkDataSet").click();
cy.get("#saveSVGDataSet").click();
cy.readFile('cypress/downloads/Modelos.svg').should('exist');
});

it("Downloads PNG file", () => {
uploadDatasetAndProceed();
cy.get("#dropdownMenuLinkDataSet").click();
cy.get("#savePNGDataSet").click();
cy.readFile('cypress/downloads/Modelos.png').should('exist');
});

it("Downloads PDF file", () => {
uploadDatasetAndProceed();
cy.get("#dropdownMenuLinkDataSet").click();
cy.get("#savePDFDataSet").click();
cy.readFile('cypress/downloads/Modelos.pdf').should('exist');
});

it("Exports model to JSON", () => {
uploadDatasetAndProceed();
cy.get("#dropdownMenuLinkDataSet").click();
cy.get("#saveJSON").click();
cy.readFile('cypress/downloads/Modelos.json').should('exist');
});

it("Exports model to TXT", () => {
uploadDatasetAndProceed();
cy.get("#dropdownMenuLinkDataSet").click();
cy.get("#saveTXT").click();
cy.readFile('cypress/downloads/Modelos.txt').should('exist');
});


it("Downloads Landscape SVG file", () => {
uploadDatasetAndProceed();
cy.get("#toggle-chart-data-set").click();
cy.get("#dropdownMenuLinkDataSetLandscape").click();
cy.get("#saveSVGDataSetLandscape").click();
cy.readFile('cypress/downloads/Modelos.svg').should('exist');
});

it("Downloads Landscape PNG file", () => {
uploadDatasetAndProceed();
cy.get("#toggle-chart-data-set").click();
cy.get("#dropdownMenuLinkDataSetLandscape").click();
cy.get("#savePNGDataSetLandscape").click();
cy.readFile('cypress/downloads/Modelos.png').should('exist');
});

it("Downloads Landscape PDF file", () => {
uploadDatasetAndProceed();
cy.get("#toggle-chart-data-set").click();
cy.get("#dropdownMenuLinkDataSetLandscape").click();
cy.get("#savePDFDataSetLandscape").click();
cy.readFile('cypress/downloads/Modelos.pdf').should('exist');
});
});
});
Binary file added cypress/fixtures/Modelos.zip
Binary file not shown.
Loading