The Argumentation Designer is a web-based tool for building, visualising, and analysing abstract and quantitative argumentation frameworks. It combines an interactive graph editor, an APX-like textual interface, and services to compute both extension-based and gradual semantics.
WEB APP: https://argumentation.dimes.unical.it/
- Project Overview and Architecture
- Project Structure, Requirements and Setup
- Deployment
- Frontend Components
- Backend API
- Data Formats
- Graph Rendering and Styling
- Extending the Tool
- Implementing Predisposed Features
- Testing and Debugging
- License and Credits
The Argumentation Designer is a web-based tool for building, visualizing, and analyzing abstract and quantitative argumentation frameworks. It provides an interactive graph editor that allows users to model arguments and relationships (attacks and supports) visually, and to compute both extension-based semantics (following Dung's approach) and gradual semantics for quantitative frameworks.
The main goals of the project are:
- Provide an accessible, no-installation tool for researchers, educators, and students working with formal argumentation
- Support multiple argumentation framework types (AF, BAF, WAF, QBAF, WBAF, WQBAF)
- Combine visual graph editing with textual APX-like descriptions
- Integrate with backend reasoning services for semantic computation
- Enable import/export of frameworks in standard formats
- HTML5/CSS3/JavaScript: Core web technologies, no build process required
- Cytoscape.js: Graph visualization and manipulation library
- cytoscape-html-label: Plugin for rendering HTML labels inside nodes
- html2canvas: Fallback library for PNG export when native methods are unavailable
- Python 3.x: Backend programming language
- Flask: Lightweight web framework for REST API endpoints
- Gunicorn: WSGI HTTP server for production deployment (4 workers)
- Clingo: Answer Set Programming solver for computing extension-based semantics
- NumPy: Numerical library for gradual semantics computation
- Apache 2.4: Web server and reverse proxy
- systemd: Service manager for Gunicorn process
- CORS: Cross-Origin Resource Sharing configuration for API access
The Argumentation Designer follows a client-server architecture with clear separation between the presentation layer (frontend) and the reasoning layer (backend).
┌─────────────────────────────────────────┐
│ Web Browser (Client) │
│ ┌───────────────────────────────────┐ │
│ │ HTML/CSS Interface │ │
│ │ - Graph workspace (Cytoscape) │ │
│ │ - Sidebar controls │ │
│ │ - Modal dialogs │ │
│ └───────────────────────────────────┘ │
│ ┌───────────────────────────────────┐ │
│ │ JavaScript Application │ │
│ │ - Event handlers │ │
│ │ - Graph synchronization │ │
│ │ - Import/Export logic │ │
│ │ - API client │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
│
│ HTTP/HTTPS (JSON)
▼
┌─────────────────────────────────────────┐
│ Apache Web Server (Proxy) │
│ - Serves static files (/var/www/html) │
│ - Proxies /api/* to Flask backend │
└─────────────────────────────────────────┘
│
│ http://127.0.0.1:5000
▼
┌─────────────────────────────────────────┐
│ Gunicorn WSGI Server (4 workers) │
│ ┌───────────────────────────────────┐ │
│ │ Flask Application (solveBAF.py) │ │
│ │ - Extension-based semantics │ │
│ │ - Gradual semantics │ │
│ │ - Constraint filtering │ │
│ └───────────────────────────────────┘ │
│ ┌───────────────────────────────────┐ │
│ │ Reasoning Engines │ │
│ │ - Clingo (extension semantics) │ │
│ │ - NumPy (gradual semantics) │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
The frontend is organized around the following main components:
- Graph workspace: Central canvas powered by Cytoscape.js where nodes (arguments) and edges (attacks/supports) are rendered and manipulated
- Sidebar: Right panel containing project management controls, textual description area, layout selector, and semantic computation panels
- Modal dialogs: Pop-up windows for editing individual arguments (name, weight, description) and relationships (type, weight)
- Context menus: Right-click menus on nodes and edges for quick access to Edit and Delete operations
- Import/Export handlers: File I/O logic for APX, JSON, and PNG formats
The backend exposes RESTful endpoints that accept JSON-encoded argumentation frameworks and return computed results:
- Extension-based semantics service: Receives an AF/BAF description and a semantic type (grounded, complete, preferred, stable), invokes Clingo with appropriate ASP rules, and returns all labelings
- Constraint filtering service: Takes a set of labelings and user-defined logical constraints (supporting AND, OR, NOT operators), filters the labelings accordingly, and returns the subset that satisfies all constraints
- Gradual semantics service: Receives a WAF/QBAF/WBAF/WQBAF description, semantic algorithm choice (DReLU, DDReLU, Euler, DFQuAD, MLP-based, Quadratic Energy), and parameters (epsilon, aggregation function, gamma), computes the final strength values iteratively, and returns the strength of each argument
The backend uses Clingo for ASP-based computation of extension semantics and custom Python algorithms for gradual semantics computation.
- User creates/edits graph → Frontend updates Cytoscape instance and regenerates APX-like textual description
- User requests semantic computation → Frontend packages current framework as JSON and sends HTTP POST to backend endpoint
- Apache receives request → Proxies to Gunicorn on localhost:5000
- Flask processes request → Invokes Clingo or Python computation module, runs reasoning algorithm
- Backend returns results → Sends JSON response with labelings or strengths through Apache
- Frontend displays results → Parses response, updates result list, applies color coding to graph nodes
All communication uses JSON over HTTP(S), making the system modular and allowing the frontend and backend to evolve independently. The Apache reverse proxy configuration ensures that static files and API requests are handled appropriately, while Gunicorn manages multiple worker processes for concurrent request handling.
The Argumentation Designer follows a client-server architecture with clear separation between the presentation layer (frontend) and the reasoning layer (backend).
The project is organized into two main directories: frontend static files and backend Flask application.
argumentation-designer/
├── frontend/ # Static HTML/CSS/JS files
│ ├── index.html # Main application page
│ ├── people.html # Team members page
│ ├── publications.html # Publications page
│ ├── fairstyle.css # Application styles
│ ├── init.js # Application initialization entry point
│ ├── utils.js # Validation, constants, helpers
│ ├── graph.js # Cytoscape core, graph operations
│ ├── interface.js # UI controls, modals, event handlers
│ ├── fileio.js # Import/export logic
│ ├── apicalls.js # Backend API communication
│ ├── favicon.ico # Argumentation Designer Icon
│ ├── unical-argumentation-designer.png # Argumentation Designer Logo
│ ├── unical-logo-white.png # Università della Calabria Logo
│ └── people/ # Team member photos
│ └── *.jpg # Individual photos
│
├── backend/ # Flask backend application
│ ├── solveBAF.py # Main Flask app with all endpoints
│ ├── sem/ # ASP semantic definition files
│ │ ├── grounded.dl # Grounded semantics rules
│ │ ├── complete.dl # Complete semantics rules
│ │ ├── preferred.dl # Preferred semantics rules
│ │ └── stable.dl # Stable semantics rules
│ └── requirements.txt # Python dependencies
│
└── README.md # Technical documentation
The frontend requires no build process or local installation. All dependencies are loaded via CDN:
- Cytoscape.js (3.x): Graph visualization library
- cytoscape-html-label: Plugin for HTML labels inside nodes
- html2canvas: Library for canvas-based screenshot export
These libraries are referenced directly in index.html via <script> tags pointing to CDN URLs.
Browser Compatibility:
- Chrome 90+ (recommended)
- Firefox 88+
- Edge 90+
- Safari 14+
Modern browsers with ES6 support and Canvas API are required.
The backend is a Python Flask application with the following dependencies:
Flask>=2.0.0
clingo>=5.5.0
numpy>=1.21.0
gunicorn>=20.1.0
Create a requirements.txt file with the above content for easy installation.
System Requirements:
- Python 3.8 or higher
- Clingo ASP solver (usually installed via pip or system package manager)
- Linux/Unix environment (tested on Ubuntu 20.04+)
git clone https://github.com/your-org/argumentation-designer.git
cd argumentation-designer
Copy the frontend files to your web server document root:
sudo cp -r frontend/* /var/www/html/
No further configuration is needed for the frontend.
Navigate to the backend directory and create a Python virtual environment:
cd backend
python3 -m venv venv
source venv/bin/activate
Install Python dependencies:
pip install -r requirements.txt
Create the temporary directory for APX files:
mkdir -p apx_temp
Ensure the semantic definition files are present in the sem/ directory. These files contain the ASP rules for each semantic type (grounded, complete, preferred, stable).
The Flask application uses the following default paths (defined in solveBAF.py):
- UPLOAD_FOLDER:
/var/www/compute/apx_temp(temporary APX files) - Semantics directory:
./sem/(relative to backend directory)
If deploying to a different location, update these paths in solveBAF.py:
UPLOAD_FOLDER = '/var/www/compute/apx_temp' # Update if neededFor local development and testing, run Flask directly:
python solveBAF.pyThe application will start on http://0.0.0.0:5000.
Note: This is suitable only for development. For production, use Gunicorn (see Deployment section).
Test the backend API with curl:
curl -X POST http://localhost:5000/api/computeBAF \
-H "Content-Type: application/json" \
-d '{"content": "arg(a). arg(b). att(a,b).", "semantics": "grounded"}'Expected response:
{
"results": ["in(a), ou(b)"]
}Open the frontend in your browser (e.g., http://localhost/ if served by Apache) and verify that you can create nodes, edges, and compute semantics.
Environment Variables
Currently the application does not use environment variables for configuration. All paths and settings are defined directly in solveBAF.py. For production deployments, consider externalizing configuration using environment variables or a config file.
File Permissions
Ensure the web server user (typically www-data on Ubuntu/Debian) has write permissions to the temporary directory:
sudo chown -R www-data:www-data /var/www/compute/apx_temp
sudo chmod 755 /var/www/compute/apx_tempIf pip install clingo fails, try installing via system package manager:
# Ubuntu/Debian
sudo apt-get install gringo
# Or download from potassco.orgIf the frontend cannot connect to the backend API, ensure CORS is properly configured. The Flask application should include CORS headers. You can add Flask-CORS:
pip install flask-corsThen in solveBAF.py:
from flask_cors import CORS
app = Flask(__name__)
CORS(app)Alternatively, ensure Apache proxy configuration includes proper CORS headers in fair.conf.
If port 5000 is already occupied, either:
- Stop the conflicting service
- Change the port in
solveBAF.py:app.run(host='0.0.0.0', port=5001) - Update the Apache proxy configuration accordingly
On some systems, NumPy may require compilation tools:
sudo apt-get install python3-dev build-essential
pip install numpyIf computation fails with "File semantica [...] non trovato", ensure all .dl files are present in the sem/ directory:
ls backend/sem/
# Should show: grounded.dl complete.dl preferred.dl stable.dlThese files must be created manually or copied from the repository semantic rules.
The frontend application requires a modern web browser with the following features:
Supported Browsers:
- Google Chrome 90+ (recommended for best performance)
- Mozilla Firefox 88+
- Microsoft Edge 90+
- Safari 14+
- Opera 76+
Required Browser Features:
- JavaScript ES6+ support
- HTML5 Canvas API
- Fetch API for HTTP requests
- Local Storage API (for project state)
- File API (for import/export operations)
Recommended Screen Resolution:
- Minimum: 1280x720
- Recommended: 1920x1080 or higher
For Backend Deployment:
- Linux distribution (Ubuntu 20.04+ or Debian 10+ recommended)
- Python 3.8 or higher
- 2 GB RAM minimum (4 GB recommended for larger frameworks)
- 1 GB free disk space
- Network access for API communication
Web Server:
- Apache 2.4+ with mod_proxy enabled
- Alternative: Nginx 1.18+ with proxy configuration
WSGI Server:
- Gunicorn 20.1+ (configured with 4 workers by default)
- Alternative: uWSGI
External Dependencies:
- Clingo 5.5+ (Answer Set Programming solver)
- NumPy 1.21+ (numerical computation library)
- Open port 80 (HTTP) or 443 (HTTPS) for frontend access
- Internal port 5000 for Gunicorn (not exposed externally)
- Outbound HTTPS access for CDN resources (Cytoscape.js, html2canvas)
- For graphs with 50+ nodes, allow 2-5 seconds for semantic computation
- For graphs with 100+ nodes, computation time may increase significantly
- Gunicorn worker count can be adjusted based on expected concurrent users
- Consider increasing worker count for high-traffic deployments
For production environments, the Flask application should run as a systemd service using Gunicorn as the WSGI server.
Create the file /etc/systemd/system/gunicorn.service:
[Unit]
Description=Gunicorn instance to serve Flask app
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/compute
ExecStart=/var/www/compute/venv/bin/gunicorn --workers 4 --bind 127.0.0.1:5000 solveBAF:app
[Install]
WantedBy=multi-user.targetConfiguration Notes:
--workers 4: Number of worker processes (adjust based on CPU cores and load)--bind 127.0.0.1:5000: Internal binding, not exposed externallysolveBAF:app: Module name and Flask app instance
sudo systemctl daemon-reload
sudo systemctl enable gunicorn.service
sudo systemctl start gunicorn.service
sudo systemctl status gunicorn.service# Restart after code changes
sudo systemctl restart gunicorn.service
# Stop the service
sudo systemctl stop gunicorn.service
# View logs
sudo journalctl -u gunicorn.service -fApache acts as a reverse proxy, serving static frontend files and forwarding API requests to Gunicorn.
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod headersCreate or edit /etc/apache2/sites-available/fair.conf:
<VirtualHost *:80>
ServerName argumentation.dimes.unical.it
# Serve static frontend files
DocumentRoot /var/www/html
<Directory /var/www/html>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
# Reverse proxy for API requests
ProxyPreserveHost On
ProxyPass /api http://127.0.0.1:5000/api nocanon
ProxyPassReverse /api http://127.0.0.1:5000/api
ProxyPassReverseCookieDomain 127.0.0.1 argumentation.dimes.unical.it
# Protect backend directory from direct access
<Directory /var/www/compute>
Require all denied
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>sudo a2ensite fair.conf
sudo systemctl restart apache2For production deployments, enable HTTPS using Let's Encrypt:
sudo apt-get install certbot python3-certbot-apache
sudo certbot --apache -d argumentation.dimes.unical.itCertbot will automatically:
- Obtain SSL certificate
- Configure Apache for HTTPS
- Set up automatic renewal
After SSL setup, edit the VirtualHost to redirect HTTP to HTTPS:
<VirtualHost *:80>
ServerName argumentation.dimes.unical.it
Redirect permanent / https://argumentation.dimes.unical.it/
</VirtualHost>If the frontend and backend are on different domains, add CORS headers in Apache configuration:
<Location /api>
Header set Access-Control-Allow-Origin "https://yourdomain.com"
Header set Access-Control-Allow-Methods "POST, GET, OPTIONS"
Header set Access-Control-Allow-Headers "Content-Type"
</Location>Alternatively, use Flask-CORS in solveBAF.py (see section 2).
File Permissions:
# Ensure backend code is not writable by web server
sudo chown -R root:www-data /var/www/compute
sudo chmod -R 755 /var/www/compute
# Only temp directory should be writable
sudo chown -R www-data:www-data /var/www/compute/apx_temp
sudo chmod 755 /var/www/compute/apx_tempFirewall Configuration:
# Allow only HTTP/HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enableRate Limiting: Consider implementing rate limiting for API endpoints to prevent abuse. This can be done with Apache modules:
sudo a2enmod ratelimitOr use Flask extensions like Flask-Limiter.
Input Validation: The backend already validates APX syntax and argument names. Ensure these checks remain in place and monitor logs for suspicious patterns.
# View Gunicorn service logs
sudo journalctl -u gunicorn.service -n 100
# View Apache access logs
sudo tail -f /var/log/apache2/access.log
# View Apache error logs
sudo tail -f /var/log/apache2/error.logThe backend includes a test endpoint for health checks:
curl http://localhost:5000/testcheck
# Expected response: "OK FROM THIS FILE"You can configure monitoring tools (Nagios, Prometheus, etc.) to periodically check this endpoint.
Critical files to backup regularly:
/var/www/compute/solveBAF.py- Backend application code/var/www/compute/sem/*.dl- ASP semantic definition files/var/www/html/*- Frontend static files/etc/apache2/sites-available/fair.conf- Apache configuration/etc/systemd/system/gunicorn.service- Systemd service configuration
When deploying code updates:
# 1. Pull latest changes
cd /var/www/compute
git pull origin main
# 2. Update Python dependencies if needed
source venv/bin/activate
pip install -r requirements.txt
# 3. Restart Gunicorn
sudo systemctl restart gunicorn.service
# 4. Update frontend if needed
cd /var/www/html
git pull origin main
# 5. Clear Apache cache if necessary
sudo systemctl reload apache2Ensure log rotation is configured to prevent disk space issues:
# Apache logs are usually rotated automatically
# Check /etc/logrotate.d/apache2
# For application-specific logs, create /etc/logrotate.d/argumentationThe frontend is a single-page application built with vanilla JavaScript, HTML5, and CSS3. It uses Cytoscape.js for graph visualization and manipulation, with no build process or bundler required. All JavaScript modules are loaded directly as scripts in index.html.
frontend/
├── index.html # Main HTML page
├── fairstyle.css # Application styles
├── init.js # Application initialization entry point
├── graph.js # Cytoscape core, graph operations
├── interface.js # UI controls, modals, event handlers
├── fileio.js # Import/export logic
├── apicalls.js # Backend API communication
└── utils.js # Validation, constants, helpers
Application entry point executed on page load. Orchestrates the initialization sequence:
- Calls
initializeCytoscape()to create the graph instance - Calls
initializeNodeHtmlLabel()to setup HTML labels plugin - Calls
registerCytoscapeEventListeners()to wire graph events - Calls
registerInterfaceEventListeners()to wire UI events - Calls
resizePreviewCanvas()to initialize the preview overlay - Calls
initializeInterface()to reset UI state
Contains shared constants, validation functions, and helper utilities:
Constants:
- Color for nodes and edges (
NODE_COLOR_DEFAULT,NODE_BORDER_COLOR_DEFAULT, etc.) - Validation regex for node ids and descriptions (
VALID_ARGUMENT_REGEX,VALID_DESCRIPTION_REGEX) - Max/min allowed lengths and ranges for node/edge attributes (
MAX_NODE_ARGUMENT_LENGTH,MIN_NODE_WEIGHT, etc.) - Default UI values (
DEFAULT_LAYOUT,DEFAULT_SEMANTIC_EXT, etc.) - API paths and params (
API_PATH_COMPUTE_BAF,API_PATH_FILTER_LABELINGS, etc.) - File IO errors (
ERR_FILE_IMPORT,ERR_FILE_IMPORT_APX, etc.) - Parsing error messages (
ERR_GENERIC,ERR_DESC_EMPTY, etc.) - Error messages for validation failures (
ERR_NODE_DUPLICATE,ERR_NODE_EMPTY_ARGUMENT, etc.) - API calls errors (
ERR_SERVER,ERR_NETWORK, etc.)
Functions:
generateEdgeId(): Returns unique edge IDs (rel_1,rel_2, ...)initEdgeIdCounterFromGraph(): Syncs edge ID counter with existing graphdownloadBlob(blob, filename): Triggers browser download for Blob objectsvalidateArgumentName(name): Checks argument name syntax and lengthvalidateWeight(weight): Validates numeric weight range
Core Cytoscape initialization, graph operations, and synchronization with textual description.
function initializeCytoscape()Creates the Cytoscape instance with:
- Container:
#cydiv - Base node style: 50x50px circles, blue background, 1px border
- Edge styles:
type: 'attack': solid red line with triangle arrowtype: 'support': dotted blue line with triangle-tee arrow
- Edge labels: display weight if present, positioned on edge with auto-rotation
- Default layout: CoSE (force-directed)
function initializeNodeHtmlLabel()Configures cytoscape-node-html-label to render structured labels inside nodes:
- Strength: small floating value above the node (when computed)
- Name: centered, bold
- Weight: below name, small
Labels are fully HTML-based (<div class="node-label-wrapper">) and styled via CSS.
clearCytoscapeGraph(): Removes all nodes and edges, preserving current layout, zoom and stylescheckGraphEmptyState(): Shows/Hides the starting message on a empty graph workspace
Node Creation:
- User clicks on canvas background
createNode(e)checks if not in edge mode- Stores click position in
window.nodeCreationContext - Opens node modal
- On confirm, creates node at stored position
Edge Creation (two-step):
- User clicks on source node → enters edge mode
createEdge(e)setsedgeModeActive = true, stores source node- Canvas cursor changes to crosshair, preview arrow follows mouse
- User clicks on target node
- Opens edge modal with source/target context
- On confirm, creates edge and exits edge mode
Preview Canvas:
A transparent overlay canvas (#cy-preview-canvas) renders the live arrow during edge creation using drawPreviewLine().
Right-click on nodes or edges opens custom context menus with Edit/Delete actions:
openNodeMenu(e)→ shows#node-context-menuopenEdgeMenu(e)→ shows#edge-context-menu
Menu actions stored in window.nodeContextCallback and window.edgeContextCallback.
From Graph to Text:
function updateDescriptionFromGraph()Iterates over cy.nodes() and cy.edges() to generate APX-like syntax:
- Nodes:
arg(name, weight).orarg(name). - Edges:
att(source, target, weight).orsupport(source, target, weight).
Automatically called on add, remove, or data events if not editing the description textarea.
From Text to Graph:
function updateGraphFromDescription()Parses #desc-area textarea line by line using regex:
arg(...)→ creates or updates nodesatt(...)→ creates or updates attack edgessupport(...)→ creates or updates support edges
Validates:
- Syntax correctness
- Argument name constraints
- Weight ranges
- Edge endpoints existence
On errors, displays inline error messages with line numbers.
On success, if the Description is changed, update the Graph:
function applyGraphChanges(nodeMap, attackEdges, supportEdges)- Removes obsolete nodes/edges
- Adds/updates nodes and edges
- Re-applies selected layout
- Updates semantic group view (AF/BAF/WAF/QBAF/WBAF/WQBAF)
On success, if the Description isn't changed, just clean and draw the Graph usign the lastest selected Layout.
function registerCytoscapeEventListeners()Registers all Cytoscape event handlers:
add,remove,data,move→ update description textarearesize,layoutstop→ resize preview canvastapon background → node creationtapon nodes → edge creation (if in edge mode) or selectioncxttap(right-click) → context menusmouseover,mouseouton nodes → show/hide tooltip with descriptionmousemove→ reposition tooltip and preview arrowmouseover,mouseout,mousedown,mouseup,grab,drag,pan,free,viewportready,tapend→ change the cursor style
Manages all UI controls, modal dialogs, and user interactions outside the graph canvas.
function initializeInterface()Resets all UI fields to defaults:
- Clears project name, description, labelings, constraints
- Resets semantic selectors to default values (grounded, drl, epsilon=0.01, etc.)
- Clears computed results (labelings, strengths)
- Does NOT touch the Cytoscape graph
function resetAppState()Full application reset:
- Calls
clearCytoscapeGraph()to remove all nodes/edges - Calls
initializeInterface()to reset UI
Used by "New project" button and after imports.
setButtonLoading(btnId, isLoading): Shows/Hides spinner on buttons when the system is workingshowError(containerId, fieldsToHighlight = [], errorContent): Shows error messages
function updateSemanticGroupView()Determines which semantic panel to show based on graph features:
| Graph Features | Semantic Type | Label |
|---|---|---|
| No supports, no weights | Extension-based | AF |
| Has supports, no weights | Extension-based | BAF |
| No supports, has edge weights | Gradual | WAF |
| Has supports, has edge weights | Gradual | WBAF |
| Has node weights, no edge weights | Gradual | QBAF |
| Has node weights, has edge weights | Gradual | WQBAF |
Shows/hides #semantic-group-ext-based or #semantic-group-gradual accordingly.
Opening:
window.openNodeModal(pos, node)pos: click position for new nodesnode: existing node for editing
Fields:
#node-argument: argument name (required, max 50 chars, alphanumeric + underscore)#node-weight: optional weight#node-description: optional description (max 500 chars)
Validation:
function validateNodeModal(argument, weight, description)Returns {valid: boolean, error: string, field: string}.
Callback:
window.nodeModalCallback(argument, weight, description)Handles both creation and editing:
- Creation mode (
window.nodeCreationContext): adds new node at stored position - Edit mode (
window.nodeEditContext): updates existing node, handles ID changes by recreating node and moving edges
Duplicate ID check uses case-insensitive comparison.
Keyboard:
- ESC closes modal
- ENTER confirms
Opening:
window.openEdgeModal(edge)edge: existing edge for editing, ornullfor creation
Fields:
- Radio buttons: Attack or Support (required)
#edge-weight: optional weight
Validation:
function validateEdgeModal(type, weight)Checks type is selected and weight is in range.
Callback:
window.edgeModalCallback(type, weight)Handles both creation and editing:
- Creation mode (
window.edgeCreationContext): creates edge between stored source/target - Edit mode (
window.edgeEditContext): updates edge type and weight
Duplicate edge check: same source, target, and type.
function registerInterfaceEventListeners()Wires all UI button clicks and form events:
- Toolbar: New project, Import, Export, Redraw Graph
- Semantic computation: Compute Labelings, Apply Constraints, Compute Strength
- Save results: Save Labelings, Save Filtered Labelings, Save Strength
- Layout selector: applies layout on change
- Gradual semantic selector: enables/disables Params and Gamma based on algorithm
- Modal buttons: Confirm, Cancel
- Context menu actions: Edit, Delete, etc.
- Keyboard: ESC, ENTER
Handles all import/export operations for APX, JSON, and PNG formats.
APX Export:
async function generateAPXBlob()Returns a Blob containing the current #desc-area content as plain text.
JSON Export:
async function generateJSONBlob()Returns a Blob with full project metadata:
{
"metadata": {
"projectName": "...",
"exportDate": "...",
"version": "1.0",
"nodeCount": 5,
"edgeCount": 3
},
"graph": {
"nodes": [
{"id": "a", "weight": 0.5, "description": "...", "position": {"x": 100, "y": 200}}
],
"edges": [
{"id": "e1", "source": "a", "target": "b", "type": "attack", "weight": null}
]
},
"layout": "cose",
"description": "arg(a, 0.5). ..."
}PNG Export:
async function generatePNGBlob()Uses Cytoscape's built-in PNG export (cy.png()) with fallback to html2canvas if the first method fails. Exports at 3x scale, max 4096x4096px, with light background.
APX Import:
function importAPX(content)Writes content to #desc-area and calls updateGraphFromDescription().
JSON Import:
function importJSON(jsonContent)Parses JSON and restores:
- Project name
- Description textarea
- Graph nodes (with positions and descriptions)
- Graph edges
- Layout selection
Import:
async function importGraph()Uses window.showOpenFilePicker() to open system file picker, accepts .apx and .json, auto-fills project name from filename.
Fallback for older browsers: hidden <input type="file"> with change event.
Export:
async function exportGraph()Uses window.showSaveFilePicker() to open save dialog with three file type options:
- APX Description (
.apx) - JSON Full Data (
.json) - PNG Image (
.png)
Fallback for older browsers: showExportFallbackDialog() with prompt-based format selection and direct downloads.
function resetComputedResults()Clears all computed results from the UI and graph:
- Unselects and clears labelings lists
- Clears the strength textarea
- Removes
strengthdata from all nodes - Restores node colors to defaults
- Rebuilds HTML labels without strength values
function resetFilteredResults()Clears all filtered results from the UI:
- unselects and clears filtered labelings lists
- restores node colors to defaults
Called automatically before importing files or when creating a new project.
Labelings:
function saveLabelings()Saves all (unfiltered) labelings from the main list to labelings.txt.
Filtered Labelings:
function saveFilteredLabelings()Saves all filtered labelings to filtered_labelings.txt.
Final Strength:
Saves all strength to strength.txt.
Handles all communication with the Flask backend API.
async function computeLabelingsFromAPI()Flow:
- Reads current description from
#desc-area - Reads selected semantic from
#semantic-group-ext-select(grounded/complete/preferred/stable) - Sends POST request to
/api/computeBAF:{ "content": "arg(a). arg(b). att(a,b).", "semantics": "grounded" } - Parses response:
{"results": ["in(a), ou(b)", ...]} - Populates
#labelings-areawith clickable list items - On list item click, calls
applyLabelingToGraph(labeling)to color nodes
Node Coloring Logic:
function applyLabelingToGraph(labeling)Parses labeling string (e.g., "in(a), ou(b), un(c)") and applies colors:
in(X): green backgroundou(X): red backgroundun(X): yellow background- Unlabeled: default blue
async function filterLabelingsFromAPI()Flow:
- Collects all labelings from
#labelings-areaas array of arrays - Reads constraints from
#constraints-area(one per line) - Sends POST request to
/api/filterLabelings:{ "labelings": [["in(a)", "ou(b)"], ["in(b)", "ou(a)"]], "constraints": ["in(a)", "!ou(a)"] } - Parses response:
{"results": [["in(a)", "ou(b)"]]} - Populates
#filtered-labelings-areawith filtered results
Constraint Syntax:
in(a): argument a must be INou(b): argument b must be OUT!in(a): argument a must NOT be INin(a), in(b): logical AND (both must be true)in(a); ou(b): logical OR (at least one must be true)
async function computeStrengthFromAPI()Flow:
- Reads current description from
#desc-area - Collects parameters:
sem: selected algorithm (#semantic-gradual-select) → drl/ddr/eul/dfq/mlp/qenepsilon: convergence threshold (#semantic-gradual-epsilon)params: aggregation function (#semantic-gradual-params) → sum/max/deltasum/deltamaxgamma: sensitivity parameter (#semantic-gradual-gamma)
- Sends POST request to
/api/computeQBAF:{ "content": "arg(a,0.5). arg(b,0.8). att(a,b).", "sem": "drl", "params": "deltasum", "gamma": 0.5, "epsilon": 0.01 } - Parses response:
{"results": {"a": "0.623", "b": "0.745"}} - Writes results to
#strength-areaas text - Calls
colorNodesByStrength(results)to update node colors and labels
Extension-Based Semantics (Labelings)
When a user clicks on a labeling in #labelings-area or #filtered-labelings-area, the colorNodesByLabeling(labelingText) function is called.
Parsing Logic:
- Splits labeling string by commas:
"in(a), ou(b), un(c)"→["in(a)", "ou(b)", "un(c)"] - For each token, extracts state and argument using regex:
/^(in|ou|un)\(([^)]+)\)$/ - Builds a map:
{state: 'in', args: ['a']}, {state: 'ou', args: ['b']}, ...
Color Scheme:
in(X): Green (#4caf50) background, dark green (#2e7d32) borderou(X): Red (#f44336) background, dark red (#971919) borderun(X): Yellow (#ffeb3b) background, dark yellow (#b6862c) border- Unlabeled: Default blue (#2196F3) background, dark blue (#1976D2) border
Gradual Semantics (Strength Values)
When strength values are computed, the colorNodesByStrength() function applies a gradient color scheme.
Applies gradient coloring based on strength value:
- 0.0 - 0.2: dark red
- 0.2 - 0.4: light red
- 0.4 - 0.6: yellow
- 0.6 - 0.8: light green
- 0.8 - 1.0: dark green
Updates each node:
node.data('strength', value)→ stores strengthnode.style('background-color', color)→ applies color- Triggers HTML label rebuild to display strength above node name
All API calls include try-catch blocks:
- Network errors: displays alert with generic error message
- Server errors (4xx/5xx): displays alert with backend error message
- Parsing errors: logs to console and displays alert
Defined in graph.js during initializeCytoscape():
Nodes:
- Default: 50x50px circle, blue (
#2196F3), 2px dark blue border - No built-in text labels (all labels via HTML plugin)
Edges:
- Attack: solid red line, triangle arrow, 3px width
- Support: dotted blue line, triangle-tee arrow, 3px width
- Edge labels: display weight if present, white background with padding
Rendered by cytoscape-node-html-label plugin, styled via CSS in fairstyle.css:
.node-html-label {
position: absolute;
pointer-events: none;
z-index: 10;
}
.node-label-strength {
font-size: 8px;
margin-bottom: -11px;
transform: translateY(-18px);
background-color: #ffffffcc;
padding: 0 2px;
border-radius: 3px;
}
.node-label-name {
font-size: 14px;
font-weight: 500;
line-height: 1.1;
}
.node-label-weight {
font-size: 8px;
margin-top: 2px;
}Available layouts in #layout-select:
- Smart mode (CoSE): Force-directed layout optimized for readability
- Circle: Nodes arranged in a circle
- Concentric: Nodes in concentric rings by connectivity
- Grid: Regular grid arrangement
- Breadthfirst: Hierarchical layered layout
- Random: Random positioning
Layout is applied by:
cy.layout({ name: selectedLayout }).run();Layout choice is stored in JSON exports and restored on import.
The backend is a Flask application (solveBAF.py) that exposes three REST API endpoints for computing argumentation semantics. It uses Clingo for extension-based semantics and custom Python algorithms for gradual semantics. All endpoints accept and return JSON.
Base URL: http://your-server/api/
Computes extension-based semantics (grounded, complete, preferred, stable) for Abstract Argumentation Frameworks and Bipolar Argumentation Frameworks.
Request Body:
{
"content": "arg(a). arg(b). att(a,b). support(b,a).",
"semantics": "grounded"
}Parameters:
content(string, required): APX-like description of the framework- Node syntax:
arg(name).orarg(name, weight). - Edge syntax:
att(source, target).orsupport(source, target). - Edge weights are accepted but not currently used in computation
- Node syntax:
semantics(string, required): One ofgrounded,complete,preferred,stable
Response (200 OK):
{
"results": [
"in(a), ou(b)",
"in(b), ou(a)"
]
}Each string in results represents one labeling, with comma-separated argument states.
Error Response (400 Bad Request):
{
"error": "Parametri 'content' e 'semantics' richiesti"
}Error Response (500 Internal Server Error):
{
"error": "Error message from Clingo or file operations"
}Implementation Details:
- Creates a temporary
.apxfile inUPLOAD_FOLDER(/var/www/compute/apx_temp) - Writes the
contentto the temporary file - Calls
concatena_file()to merge:- Graph content (arguments and relationships)
- ASP rules for BAF reachability and extended attacks
- Semantic-specific rules from
sem/{semantics}.dl - Output statements (
#show in/1,#show out/1,#show un/1)
- Invokes Clingo via Python bindings (
clingo.Control()) - Parses all answer sets and returns them as strings
- Cleans up temporary files
Semantic Files Required:
sem/grounded.dl: ASP rules for grounded semanticssem/complete.dl: ASP rules for complete semanticssem/preferred.dl: ASP rules for preferred semanticssem/stable.dl: ASP rules for stable semantics
Filters a set of labelings based on user-defined logical constraints.
Request Body:
{
"labelings": [
["in(a)", "ou(b)", "un(c)"],
["in(b)", "ou(a)", "un(c)"],
["un(a)", "un(b)", "in(c)"]
],
"constraints": [
"in(a)",
"!ou(a)"
]
}Parameters:
labelings(array of arrays, required): Each inner array is a labeling represented as list of stringsconstraints(array of strings, required): Logical formulas in propositional logic
Constraint Syntax:
- Atomic propositions:
in(a),ou(b),un(c) - Negation:
!in(a)(NOT operator) - Conjunction:
in(a), in(b)(AND operator, comma-separated) - Disjunction:
in(a); ou(b)(OR operator, semicolon-separated) - Parentheses:
(in(a), ou(b)); in(c)for grouping
Response (200 OK):
{
"results": [
["in(a)", "ou(b)", "un(c)"]
]
}Returns only labelings that satisfy ALL constraints.
Error Response (400 Bad Request):
{
"error": "Parametri 'labelings' e 'constraints' richiesti"
}Error Response (500 Internal Server Error):
{
"error": "Constraint parsing error or evaluation error"
}Implementation Details:
- Parses each constraint string into an Abstract Syntax Tree (AST) using recursive descent parser
- AST nodes:
AtomicProposition,NotOperator,AndOperator,OrOperator - For each labeling, converts it to a set and evaluates all constraint AST nodes
- Returns only labelings where all constraints evaluate to
True
Operator Precedence:
- Parentheses (highest)
- NOT
! - AND
, - OR
;(lowest)
Computes gradual semantics for Quantitative Bipolar Argumentation Frameworks using iterative algorithms.
Request Body:
{
"content": "arg(a,0.5). arg(b,0.8). arg(c,0.3). att(a,b). support(c,b).",
"sem": "drl",
"params": "deltasum",
"gamma": 0.5,
"epsilon": 0.01,
"verbose": false
}Parameters:
content(string, required): APX-like description with node weights- Node syntax:
arg(name, weight).where weight ∈[0,1] - Edge syntax:
att(source, target).orsupport(source, target).
- Node syntax:
sem(string, required): Semantic algorithm, one of:drl: DReLU (Differentiable ReLU)ddr: DDReLU (Double Differentiable ReLU)eul: Euler-baseddfq: DFQuADmlp: MLP-based (sigmoid activation)qen: Quadratic Energy
params(string, optional): Aggregation function for attacks and supportssum: simple summationmax: maximum valuedeltasum: delta-based sumdeltamax: delta-based max- Ignored by
eul,dfq,mlpsemantics
gamma(float, optional, default: 0.5): Sensitivity parameter ∈[0,1]- Used by
drlandddrsemantics - Ignored by
eul,dfq,qen,mlp
- Used by
epsilon(float, optional, default: 0.01): Convergence threshold- Iteration stops when |strength[t] - strength[t-1]| < epsilon for all arguments
verbose(boolean, optional, default: false): If true, prints iteration details to console
Response (200 OK):
{
"results": {
"a": "0.623",
"b": "0.745",
"c": "0.412"
}
}Returns final strength values for each argument, formatted with 3 decimal places.
Error Response (400 Bad Request):
{
"error": "Parametri 'content' e 'sem' richiesti"
}Error Response (500 Internal Server Error):
{
"error": "Parsing error or computation error"
}Implementation Details:
- Parses
contentusingparse_qbaf_from_string():- Extracts initial scores (node weights)
- Builds attacker and supporter dictionaries
- Initializes strength values with initial scores
- Iterates until convergence (epsilon threshold) or max iterations:
- For each argument, computes aggregated attack and support influence
- Applies semantic-specific update function
- Returns final strength values
Semantic Algorithms:
-
DReLU (
drl): Differentiable ReLU with gamma weighting- Formula:
max(-1, min(1, (2*tau - 1 + delta) * gamma + (1-gamma)))/2
- Formula:
-
DDReLU (
ddr): Double Differentiable ReLU using exponential smoothing- Formula: Uses log-sum-exp for smooth approximation
-
Euler (
eul): Euler-based update- Formula:
1 - (1 - tau)² + (1 + tau) * e^alpha
- Formula:
-
DFQuAD (
dfq): Delta-based quadratic- Formula:
tau + tau * min(0, alpha) + (1-tau) * max(0, alpha)
- Formula:
-
MLP (
mlp): Sigmoid activation- Formula:
1 / (1 + e^(-alpha))
- Formula:
-
Quadratic Energy (
qen): Energy-based aggregation- Formula:
E * (1 - E) * tauwhereE = agg² / (1 + agg²)
- Formula:
Aggregation Functions:
alpha_plus(support): aggregates support influencesalpha_minus(attack): aggregates attack influencesaggregation=alpha_plus - alpha_minus(adjusted by aggregation mode)
Modes:
product: uses1 - scoreproductssum: simple summationdeltamax: normalized by max(alpha_plus, alpha_minus)deltasum: normalized by alpha_plus + alpha_minus
- 200 OK: Request successful, results returned
- 400 Bad Request: Missing or invalid parameters
- 404 Not Found: Semantic definition file not found (extension-based only)
- 500 Internal Server Error: Clingo error, parsing error, or Python exception
Extension-Based Semantics:
- Missing semantic file:
"File semantica 'sem/grounded.dl' non trovato" - Invalid APX syntax: Clingo parsing errors
- File system errors: Permission denied on temp directory
Constraint Filtering:
- Invalid constraint syntax:
"Constraint non valido o formato non gestito: [formula]" - Malformed labeling arrays: Type errors
Gradual Semantics:
- Invalid node weight: Out of range[0,1]
- Missing argument in edges: References undefined argument
- Invalid semantic name: Unsupported
semvalue - Convergence timeout: Exceeds maximum iterations (implementation-dependent)
The Flask application uses print() statements with flush=True for immediate console output. Key log messages:
"computeBAF CALLED": Extension-based computation started"filterLabelings API CALLED": Constraint filtering started"Total number of iterations: N": Gradual semantic convergence info (if verbose=true)
Logs can be viewed via:
sudo journalctl -u gunicorn.service -fSimple health check endpoint for monitoring.
Response (200 OK):
OK FROM THIS FILE
No authentication required. Can be used by monitoring tools to verify the backend is responding.
The APX (Abstract Argumentation Problems eXchange) format is a text-based representation for argumentation frameworks. The tool uses an extended APX-like syntax that supports both classical AF and extensions (AF, BAF, WAF, QBAF, WBAF, WQBAF).
Arguments:
arg(name).
arg(name, weight).
name: Alphanumeric identifier (lowercase recommended for consistency)- Allowed characters:
a-z,A-Z,0-9,_ - Maximum length: 50 characters
- Case-insensitive internally (converted to lowercase)
- Allowed characters:
weight: Optional float value in range[0,1] (default 1)- Used for QBAF/WQBAF frameworks
- Represents initial base score of the argument
Attack Relationships:
att(source, target).
att(source, target, weight).
source: Name of attacking argumenttarget: Name of attacked argumentweight: Optional float value in range[0,1] (default 1)- Used for WAF/QBAF/WBAF/WQBAF support
Support Relationships:
support(source, target).
support(source, target, weight).
source: Name of supporting argumenttarget: Name of supported argumentweight: Optional float value in range[0,1]- Used for WAF/QBAF/WBAF/WQBAF support
arg(a).
arg(b, 0.7).
arg(c, 0.5).
att(a, b).
att(b, c).
support(c, a).
This represents a QBAF with three arguments where:
- Argument
ahas no explicit weight (defaults to unweighted) - Argument
bhas initial weight 0.7 - Argument
chas initial weight 0.5 aattacksbbattacksccsupportsa
- Each statement must end with a period
. - Arguments must be declared before being used in relationships
- Duplicate argument declarations are not allowed (case-insensitive)
- Empty lines and whitespace are ignored
- Comments are not supported
The tool automatically detects the framework type based on content:
| Framework Type | Detection Criteria |
|---|---|
| AF (Abstract Argumentation) | Only attacks, no supports, no weights |
| BAF (Bipolar) | Has both attacks and supports, no weights |
| WAF (Weighted) | Only attacks, has edge weights, no node weights |
| WBAF (Weighted Bipolar) | Has both attacks and supports, has edge weights, no node weights |
| QBAF (Quantitative Bipolar) | Has node weights, may have supports |
| WQBAF (Weighted Quantitative) | Has node weights and edge weights, may have supports |
The JSON export format preserves complete project state including graph layout, node positions, descriptions, and all metadata.
{
"metadata": {
"projectName": "string",
"exportDate": "ISO8601 timestamp",
"version": "1.0",
"nodeCount": integer,
"edgeCount": integer
},
"graph": {
"nodes": [
{
"id": "string",
"weight": float or null,
"description": "string or empty",
"position": {
"x": float,
"y": float
}
}
],
"edges": [
{
"id": "string",
"source": "string",
"target": "string",
"type": "attack" | "support",
"weight": float or null
}
]
},
"layout": "string",
"description": "string"
}Metadata Section:
projectName: User-defined project name (from#project-nameinput)exportDate: Timestamp of export in ISO 8601 formatversion: Format version (currently "1.0")nodeCount: Total number of arguments in the graphedgeCount: Total number of relationships in the graph
Graph.Nodes:
id: Unique argument identifier (lowercase, alphanumeric + underscore)weight: Argument weight ornullif not set[0,1]description: Optional textual description (max 500 chars)- Note: Descriptions are NOT exported to APX format
- Only preserved in JSON exports
position: Absolute x, y coordinates in the canvas- Preserves node layout when reimporting
Graph.Edges:
id: Unique edge identifier (e.g., "e1", "e2", ...)source: ID of source argumenttarget: ID of target argumenttype: Either"attack"or"support"weight: Edge weight ornullif not set[0,1]
Top-Level Fields:
layout: Selected layout algorithm name- Values:
"cose","circle","concentric","grid","breadthfirst","random" - Restored when importing the project
- Values:
description: Full APX-like textual description- Content of
#desc-areatextarea at export time
- Content of
{
"metadata": {
"projectName": "Example BAF",
"exportDate": "2026-01-16T12:00:00.000Z",
"version": "1.0",
"nodeCount": 3,
"edgeCount": 3
},
"graph": {
"nodes": [
{
"id": "a",
"weight": null,
"description": "First argument",
"position": {"x": 150, "y": 200}
},
{
"id": "b",
"weight": 0.7,
"description": "",
"position": {"x": 300, "y": 200}
},
{
"id": "c",
"weight": 0.5,
"description": "Third argument with weight",
"position": {"x": 225, "y": 350}
}
],
"edges": [
{
"id": "e1",
"source": "a",
"target": "b",
"type": "attack",
"weight": null
},
{
"id": "e2",
"source": "b",
"target": "c",
"type": "attack",
"weight": null
},
{
"id": "e3",
"source": "c",
"target": "a",
"type": "support",
"weight": null
}
]
},
"layout": "cose",
"description": "arg(a). arg(b, 0.7). arg(c, 0.5). att(a, b). att(b, c). support(c, a)."
}When importing a JSON file:
- Project name is restored from
metadata.projectName - Description textarea is populated from
descriptionfield - Graph is cleared and rebuilt from
graph.nodesandgraph.edges - Node positions are restored from
positiondata - Node descriptions are restored (invisible in graph, shown in tooltips)
- Layout selector is set to
layoutvalue - The selected layout is applied to the graph
Extension-based semantic computation returns labelings as arrays of strings.
{
"results": [
"in(arg1), ou(arg2), un(arg3)",
"in(arg2), ou(arg1), un(arg3)"
]
}Each labeling is a comma-separated list of argument states:
in(X): Argument X is IN (accepted)ou(X): Argument X is OUT (rejected)un(X): Argument X is UNDECIDED
Example:
"in(a), in(c), ou(b), un(d)"
Represents a labeling where:
- Arguments
aandcare accepted - Argument
bis rejected - Argument
dis undecided
The frontend parses each labeling string to extract argument states:
- Splits by comma and whitespace
- Extracts state and argument name using regex
- Stores as clickable list items in
#labelings-area - On click, applies color coding to graph:
in(X): green background, green borderou(X): red backgroundun(X): yellow background- Unlabeled: default blue
The constraint filtering endpoint returns the same format:
{
"results": [
"in(a), ou(b), un(c)"
]
}Results are displayed in #filtered-labelings-area with the same parsing and coloring logic.
Gradual semantic computation returns a mapping of argument names to numerical strength values.
{
"results": {
"arg1": "0.623",
"arg2": "0.745",
"arg3": "0.412"
}
}- Keys: Argument names (lowercase identifiers)
- Values: Strength values as strings formatted with 3 decimal places
- Range: [0.000, 1.000]
- Represents final computed strength after convergence
{
"results": {
"a": "0.500",
"b": "0.673",
"c": "0.289"
}
}Interpretation:
- Argument
ahas strength 0.5 (neutral) - Argument
bhas strength 0.673 (relatively strong) - Argument
chas strength 0.289 (relatively weak)
The frontend processes strength results:
-
Writes results to
#strength-areatextarea as formatted text:a: 0.500 b: 0.673 c: 0.289 -
Applies gradient coloring to graph nodes based on strength:
- [0.0 - 0.2]: Dark red (#d32f2f)
- [0.2 - 0.4]: Light red (#e57373)
- [0.6 - 0.8]: Light green (#81c784)
- [0.8 - 1.0]: Dark green (#4caf50)
-
Updates HTML labels to display strength above node name:
<div class="node-label-strength">0.673</div> <div class="node-label-name">b</div>
-
Stores strength in node data:
node.data('strength', 0.673)
If verbose: true is passed in the request, the backend prints iteration details to console logs but does NOT include them in the JSON response. The response format remains the same regardless of verbose setting.
The graph visual appearance is controlled by a Cytoscape stylesheet defined in graph.js during initialization. The stylesheet uses CSS-like syntax and is applied programmatically.
Default Node Appearance:
{
selector: 'node',
style: {
'background-color': NODE_COLOR_DEFAULT, // #2196F3 (blue)
'width': 50,
'height': 50,
'border-width': 2,
'border-color': NODE_BORDER_COLOR_DEFAULT, // #1976D2 (dark blue)
'color': '#fff'
}
}- Nodes are circular by default (no shape specified = circle)
- Fixed size: 50x50 pixels
- Blue background with darker blue border
- White text color (not used since labels are HTML-based)
No Built-in Labels:
The stylesheet does NOT define a label property for nodes. All node labels are rendered via the cytoscape-node-html-label plugin, which overlays HTML content on top of the graph.
Attack Edges:
{
selector: 'edge[type="attack"]',
style: {
'line-color': EDGE_ATTACK_COLOR, // #b71918 (red)
'target-arrow-color': EDGE_ATTACK_COLOR,
'target-arrow-shape': 'triangle',
'width': 3,
'line-style': 'solid',
'curve-style': 'bezier'
}
}- Solid red lines
- Triangular arrowhead pointing to target
- 3px line width
- Bezier curves for smooth routing
Support Edges:
{
selector: 'edge[type="support"]',
style: {
'line-color': EDGE_SUPPORT_COLOR, // #1976D2 (blue)
'target-arrow-color': EDGE_SUPPORT_COLOR,
'target-arrow-shape': 'triangle-tee',
'width': 3,
'line-style': 'dotted',
'curve-style': 'bezier'
}
}- Dotted blue lines (distinguishes from attacks)
- Triangle-tee arrowhead (⊥ shape)
- Same width as attacks
- Bezier curves
Edge Labels (Weight Display):
{
selector: 'edge',
style: {
'label': function(ele) {
const weight = ele.data('weight');
return (weight !== null && weight !== undefined) ? weight : '';
},
'font-size': 10,
'font-weight': 400,
'color': '#333',
'text-background-color': '#fff',
'text-background-padding': 3,
'text-background-shape': 'roundrectangle',
'text-background-opacity': 0.8,
'text-margin-x': -3,
'text-margin-y': -3,
'edge-text-rotation': 'autorotate'
}
}- Edge weight displayed on the edge if present
- White rounded background for readability
- Auto-rotates with edge angle
- Only shows if weight is defined
Node colors are updated dynamically via JavaScript when:
- A labeling is selected (extension-based semantics)
- Strength values are computed (gradual semantics)
These updates use node.style() method to override default colors.
The tool provides six layout algorithms accessible via the #layout-select dropdown.
Algorithm: Compound Spring Embedder (force-directed)
Use Case:
- Default layout
- General-purpose, works well for most graphs
- Automatically minimizes edge crossings and node overlaps
Configuration:
cy.layout({
name: 'cose',
animate: true,
animationDuration: 500
}).run();When to Use:
- Medium-sized graphs (5-50 nodes)
- When you want automatic optimal positioning
- When graph structure is not hierarchical
Algorithm: Positions nodes in a circle
Use Case:
- Emphasizes graph symmetry
- Good for small graphs with clear cycle structures
- Useful for pedagogical examples
When to Use:
- Small graphs (< 10 nodes)
- When all nodes have similar importance
- When demonstrating cycles or symmetrical attacks
Algorithm: Nodes arranged in concentric rings based on connectivity
Use Case:
- Highlights central vs. peripheral arguments
- Nodes with more connections placed in inner rings
When to Use:
- Graphs with clear centrality structure
- When you want to emphasize hub arguments
- Bipolar frameworks with central mediators
Algorithm: Regular grid arrangement
Use Case:
- Uniform spacing
- Easy to count nodes
- Clean, organized appearance
When to Use:
- Presentation or export to images
- When structure is less important than clarity
- Large graphs where you need predictable spacing
Algorithm: Hierarchical layered layout
Use Case:
- Tree-like or hierarchical argumentation
- Shows levels of derivation
When to Use:
- Acyclic graphs
- Frameworks with clear attack chains
- When demonstrating grounded semantics step-by-step
Algorithm: Uniformly random positioning
Use Case:
- Starting point for manual arrangement
- Breaking out of local minima in force-directed layouts
When to Use:
- When other layouts fail
- As a reset before applying another layout
- For testing purposes
Layouts are applied by:
- User selects layout from dropdown
#layout-selectchange event fires- JavaScript reads selected value
- Calls
cy.layout({ name: selectedLayout }).run() - Cytoscape animates nodes to new positions
Layout Persistence:
- Selected layout is stored in JSON exports (
"layout": "cose") - When importing JSON, layout is restored and re-applied
- Node positions from JSON override layout algorithm
Node labels are rendered as HTML overlays using the cytoscape-node-html-label plugin.
function initializeNodeHtmlLabel() {
if (htmlLabel && typeof htmlLabel.destroy === 'function') {
try {
htmlLabel.destroy();
} catch (e) {
console.warn('Error destroying htmlLabel', e);
}
}
htmlLabel = cy.nodeHtmlLabel([
{
query: 'node',
halign: 'center',
valign: 'center',
halignBox: 'center',
valignBox: 'center',
cssClass: 'node-html-label',
tpl: function(data) {
const name = data.id;
const strength = (data.strength !== null && data.strength !== '') ? data.strength : '';
const weight = (data.weight !== null && data.weight !== '') ? data.weight : '';
return `<div class="node-label-wrapper">
${strength !== '' ? `<div class="node-label-strength">${strength}</div>` : ''}
<div class="node-label-name">${name}</div>
${weight !== '' ? `<div class="node-label-weight">${weight}</div>` : ''}
</div>`;
}
}
]);
}Each node label consists of three optional parts rendered as HTML:
<div class="node-label-wrapper">
<div class="node-label-strength">0.673</div> <!-- Optional: only if strength computed -->
<div class="node-label-name">b</div> <!-- Always present: argument name -->
<div class="node-label-weight">0.5</div> <!-- Optional: only if weight defined -->
</div>Wrapper:
.node-html-label {
position: absolute;
transform: translate(-50%, -50%); /* Center on node */
pointer-events: none;
z-index: 10;
}
.node-label-wrapper {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
color: var(--bg-white); /* White text */
pointer-events: none;
}Strength (floating above):
.node-label-strength {
font-size: 8px;
font-weight: 400;
margin-bottom: -11px;
transform: translateY(-18px); /* Lift above node */
color: var(--text-dark); /* Dark text */
background-color: #ffffffcc; /* Semi-transparent white bg */
padding: 0 2px;
border-radius: 3px;
}Name (centered):
.node-label-name {
font-size: 14px;
font-weight: 500;
line-height: 1.1;
}Weight (below name):
.node-label-weight {
font-size: 8px;
font-weight: 400;
margin-top: 2px;
}Labels are automatically updated when:
- Node data changes (
data.strength,data.weight) initializeNodeHtmlLabel()is called- Graph is redrawn after description update
The plugin re-evaluates the tpl function for each node and renders fresh
The tool can be extended to support additional argumentation semantics by adding both frontend controls and backend computation logic.
Extension-based semantics (like grounded, complete, preferred, stable) require both ASP rules and frontend integration.
Backend Steps:
-
Create ASP Rule File: Create a new
.dlfile inbackend/sem/directory:cd backend/sem nano newsemantic.dl -
Define ASP Rules: Write the Answer Set Programming rules for your semantic. Follow the pattern from existing files:
% Example: Semi-stable semantics in(X) :- argu(X), not out(X). out(X) :- argu(X), atta(Y,X), in(Y). % Add semantic-specific constraints :- argu(X), not in(X), not out(X), not cycle(X). -
Test ASP Rules: Test your rules with Clingo directly:
clingo test_graph.apx newsemantic.dl
Frontend Steps:
-
Add Option to Selector: Edit
index.htmland add a new option to#semantic-group-ext-select:<select id="semantic-group-ext-select"> <option value="grounded">Grounded</option> <option value="complete">Complete</option> <option value="preferred">Preferred</option> <option value="stable">Stable</option> <option value="newsemantic">New Semantic</option> </select>
-
No JavaScript Changes Required: The
computeLabelingsFromAPI()function inapicalls.jsautomatically reads the selected value and sends it to the backend. No code changes needed. -
Test Integration:
- Select the new semantic from dropdown
- Click "Compute Labelings"
- Verify results appear in labelings area
Gradual semantics require implementing iterative update functions in the Python backend.
Backend Steps:
-
Implement Update Function: Edit
solveBAF.pyand add a new function following the pattern:def new_semantic(a, t, attackers, supporters, score, params=None, gamma=None): """ Computes strength update for argument a at iteration t. Args: a: argument name t: current iteration attackers: dict mapping args to lists of attackers supporters: dict mapping args to lists of supporters score: dict mapping args to lists of strength values per iteration params: aggregation mode (sum/max/deltasum/deltamax) gamma: sensitivity parameter Returns: float: updated strength value in [0, 1] """ tau_a = score[a] # Initial base score # Compute aggregated influence alpha = aggregation(a, t, attackers, supporters, score, params) # Apply your semantic-specific formula result = tau_a + 0.5 * alpha # Example formula # Ensure result is in [0, 1] return max(0, min(1, result))
-
Register Semantic: The function name becomes the semantic identifier. Users will call it with
"sem": "new_semantic". -
Update Documentation: Add description of the new semantic in comments:
# new_semantic: Custom semantic description # - Uses simple linear combination # - Ignores gamma parameter # - Requires params for aggregation
Frontend Steps:
-
Add Option to Selector: Edit
index.htmland add option to#semantic-gradual-select:<select id="semantic-gradual-select"> <option value="drl">DReLU</option> <option value="ddr">DDReLU</option> <option value="eul">Euler</option> <option value="dfq">DFQuAD</option> <option value="mlp">MLP-based</option> <option value="qen">Quadratic Energy</option> <option value="new_semantic">New Semantic</option> </select>
-
Update Control Logic (if needed): If your semantic doesn't use certain parameters, update
updateSemanticGradualControls()ininterface.js:function updateSemanticGradualControls() { const semanticSelect = document.getElementById('semantic-gradual-select'); const paramsSelect = document.getElementById('semantic-gradual-params'); const gammaInput = document.getElementById('semantic-gradual-gamma'); if (!semanticSelect || !paramsSelect || !gammaInput) return; const selectedSemantic = semanticSelect.value; // Disable gamma for specific semantics const gammaDisabled = ['eul', 'dfq', 'qen', 'mlp', 'new_semantic'].includes(selectedSemantic); gammaInput.disabled = gammaDisabled; gammaInput.classList.toggle('disabled-control', gammaDisabled); // Disable params for specific semantics const paramsDisabled = ['eul', 'dfq', 'mlp'].includes(selectedSemantic); paramsSelect.disabled = paramsDisabled; paramsSelect.classList.toggle('disabled-control', paramsDisabled); }
-
Test Integration:
- Create QBAF graph with node weights
- Select new semantic
- Set parameters (epsilon, params, gamma)
- Click "Compute Strength"
- Verify strength values appear and colors are applied
Cytoscape.js supports many built-in layouts. Adding a new one only requires frontend changes.
Steps:
-
Check Cytoscape Documentation: Visit https://js.cytoscape.org/#layouts to see available layouts and their options.
-
Add Option to Selector: Edit
index.htmland add to#layout-select:<select id="layout-select"> <option value="cose">Smart mode</option> <option value="circle">Circle</option> <option value="concentric">Concentric</option> <option value="grid">Grid</option> <option value="breadthfirst">Breadthfirst</option> <option value="random">Random</option> <option value="dagre">Dagre (Hierarchical)</option> </select>
-
Install Additional Layout (if external): Some layouts require additional plugins. For example, Dagre:
<script src="https://unpkg.com/dagre@0.8.5/dist/dagre.min.js"></script> <script src="https://unpkg.com/cytoscape-dagre@2.5.0/cytoscape-dagre.js"></script>
-
Register Extension (if needed): Add to
init.js:cytoscape.use(cytoscapeDagre);
-
Custom Layout Options: If the layout needs specific configuration, update the layout application code in
interface.js:document.getElementById('layout-select').addEventListener('change', function() { const selectedLayout = this.value; let layoutOptions = { name: selectedLayout }; // Custom options per layout if (selectedLayout === 'dagre') { layoutOptions.rankDir = 'TB'; // Top to bottom layoutOptions.nodeSep = 50; layoutOptions.rankSep = 100; } cy.layout(layoutOptions).run(); });
-
Test:
- Create a graph
- Select new layout
- Verify nodes are positioned correctly
- Test with different graph sizes and structures
The user interface can be customized by modifying HTML structure and CSS styles.
Global Color Scheme:
Edit fairstyle.css root variables:
:root {
--primary: #b71918; /* Main red color */
--secondary: #9d9d9d; /* Gray */
--text-dark: #111; /* Text color */
--bg-light: #f3f3f3; /* Light background */
--bg-white: #fff; /* White background */
--footer-dark: #9d9d9d; /* Footer color */
}Change these values to customize the entire application theme.
Node Colors:
Edit constants in utils.js:
const NODE_COLOR_DEFAULT = '#2196F3'; // Default blue
const NODE_BORDER_COLOR_DEFAULT = '#1976D2'; // Border blue
const EDGE_ATTACK_COLOR = '#b71918'; // Attack red
const EDGE_SUPPORT_COLOR = '#1976D2'; // Support blueAdd Background Grid:
Edit Cytoscape initialization in graph.js:
cy = cytoscape({
container: document.getElementById('cy'),
style: [
{
selector: 'core',
style: {
'active-bg-color': '#e0e0e0',
'active-bg-opacity': 0.3
}
},
// ... rest of styles
]
});
// Add grid background via CSS
document.getElementById('cy').style.backgroundImage =
'linear-gradient(#ddd 1px, transparent 1px), linear-gradient(90deg, #ddd 1px, transparent 1px)';
document.getElementById('cy').style.backgroundSize = '20px 20px';The application includes a partially implemented feature that can be completed with minimal effort. Frontend UI elements that are currently commented out and require backend extensions to become fully functional.
Current State:
The filtering system currently supports only hard constraints. The UI contains a commented-out textarea for preferences, and the button label references both constraints and preferences, but only constraints are processed.
Conceptual Workflow:
The completed feature should work as follows:
- User computes extension-based labelings (grounded, complete, preferred, stable)
- User enters hard constraints in the "Constraints" textarea (e.g.,
in(a),!ou(b)) - User enters soft preferences in the "Preferences" textarea (e.g.,
in(c) > ou(d)) - User clicks "Apply Constraints + Preferences"
- Backend filters out labelings that violate any constraint (hard requirement)
- Backend ranks remaining labelings by how many preferences they satisfy
- Frontend displays filtered and ranked labelings in order of preference satisfaction
Implementation Requirements:
Frontend Changes:
- Locate and uncomment the preferences textarea in
index.html - Verify to ise the button label correctly mentioning both constraints and preferences
- Modify the API call function in
apicalls.jsto collect preferences from the textarea - Include preferences array in the JSON request body sent to the backend
- Update the interface initialization function to reset the preferences textarea
Backend Changes:
- Extend the
/api/filterLabelingsendpoint to accept an optionalpreferencesparameter - Keep the existing constraint filtering logic (this is the hard filter)
- Add a ranking function that scores each filtered labeling by counting how many preferences it satisfies
- Use the same AST parsing logic already implemented for constraints
- Sort labelings by preference score in descending order
- Return the sorted list to the frontend
Semantic Difference:
- Constraints: Boolean conditions that must be satisfied (a labeling either passes or fails)
- Preferences: Soft criteria used for ranking (labelings satisfy 0, 1, 2, ... preferences)
- All returned labelings satisfy all constraints
- Labelings are ordered by preference satisfaction (most preferred first)
Testing Strategy:
- Create a graph with multiple labelings (e.g., complete semantics often yields several solutions)
- Add a constraint that eliminates some labelings
- Add preferences that differentiate between remaining labelings
- Verify that only constraint-satisfying labelings appear
- Verify that labelings are ordered by preference count
The browser developer console is the primary debugging tool for the frontend application.
Opening the Console:
- Chrome/Edge: F12 or Ctrl+Shift+J (Windows/Linux), Cmd+Option+J (Mac)
- Firefox: F12 or Ctrl+Shift+K (Windows/Linux), Cmd+Option+K (Mac)
- Safari: Cmd+Option+C (Mac, requires enabling Developer menu first)
Key Console Features:
- Console tab: View logged messages, errors, and warnings
- Network tab: Monitor API requests and responses (check
/api/computeBAF,/api/computeQBAF,/api/filterLabelings) - Sources/Debugger tab: Set breakpoints in JavaScript files
- Elements/Inspector tab: Inspect HTML structure and CSS styles
Common Console Messages:
"APX file imported successfully": Successful import"Validation errors: ...": Description parsing failed"computeBAF CALLED": Backend received extension-based request"Error importing JSON": JSON format issue- Cytoscape warnings about edge operations (known library bug, can be ignored)
Debugging Workflow:
- Reproduce the issue
- Open console and check for error messages
- Use Network tab to verify API requests/responses
- Set breakpoints in relevant JavaScript files
- Inspect variables and call stack
- Test fixes and verify in console
"Argument name is required"
- Cause: Empty argument name in node modal
- Solution: Enter a valid alphanumeric name
"Argument name already exists"
- Cause: Duplicate node ID (case-insensitive)
- Solution: Choose a different name
"Line X: UNKNOWN SYNTAX"
- Cause: Invalid APX syntax in description textarea
- Solution: Check syntax (missing period, invalid characters, typos in
arg,att,support)
"Line X: Source/Target argument not declared"
- Cause: Edge references non-existent node
- Solution: Declare all arguments before using them in relationships
"No labelings to filter"
- Cause: Trying to filter before computing labelings
- Solution: Click "Compute Labelings" first
Modal won't close
- Cause: JavaScript error or event listener issue
- Solution: Press ESC key or refresh page
"File semantica 'sem/X.dl' non trovato"
- Cause: Missing semantic definition file
- Solution: Ensure all
.dlfiles exist inbackend/sem/directory
"Parametri 'content' e 'semantics' richiesti"
- Cause: Malformed API request
- Solution: Check frontend API call includes all required parameters
"Constraint non valido"
- Cause: Invalid constraint syntax
- Solution: Use correct syntax (
in(a),ou(b),un(c),!,,,;)
Clingo parsing errors
- Cause: Invalid APX content sent to backend
- Solution: Validate description syntax on frontend before sending
CORS errors
- Cause: Frontend and backend on different domains without CORS headers
- Solution: Configure Apache proxy correctly or add Flask-CORS
Connection refused
- Cause: Gunicorn service not running
- Solution:
sudo systemctl start gunicorn.service
API request timeout
- Cause: Large graph or slow computation
- Solution: Increase timeout, optimize graph size, check backend logs
502 Bad Gateway
- Cause: Gunicorn crashed or Apache can't reach backend
- Solution: Check Gunicorn logs, restart service
JavaScript:
- Use camelCase for variables and functions
- Use descriptive names (
updateGraphFromDescriptionnotupdate) - Add comments for complex logic
- Keep functions focused and modular
- Use
constfor constants,letfor variables - Avoid global variables (use
window.prefix if necessary)
Python:
- Follow PEP 8 style guide
- Use snake_case for functions and variables
- Add docstrings for functions
- Handle exceptions explicitly
- Use meaningful variable names
HTML/CSS:
- Use semantic HTML elements
- Keep CSS classes descriptive (
node-label-strengthnotnls) - Use CSS variables for colors (defined in
:root) - Keep inline styles minimal
- Create, edit, delete nodes via click and modal
- Create, edit, delete edges via two-click workflow
- Update description textarea after graph changes
- Rebuild graph from description textarea
- Apply different layouts (Smart mode, Circle, Grid, etc.)
- Context menus work on right-click
- Export APX file and verify content
- Export JSON file and verify structure
- Export PNG image and verify quality
- Import APX file and verify graph recreation
- Import JSON file and verify layout/descriptions preserved
- Project name auto-fills from filename
- Compute labelings for AF (grounded, complete, preferred, stable)
- Compute labelings for BAF with supports
- Click on labeling to color graph
- Save labelings to file
- Apply constraints and filter labelings
- Save filtered labelings to file
- Create QBAF with node weights
- Compute strength for all semantics (drl, ddr, eul, dfq, mlp, qen)
- Verify strength values in range[0,1]
- Verify gradient coloring applied
- Verify strength displayed above node name
- Save strength results to file
- Parameters (epsilon, params, gamma) enable/disable correctly
- AF detected (no supports, no weights)
- BAF detected (with supports, no weights)
- QBAF detected (with node weights)
- Semantic panels switch automatically
- Empty argument name shows error
- Duplicate argument name shows error
- Invalid weight range shows error
- Invalid description syntax shows line-specific errors
- Network errors show user-friendly alert
- Backend errors displayed in alert
- Graph with single node works
- Graph with 50+ nodes renders and computes
- Self-loops handled correctly
- Multiple edges between same nodes prevented
- Empty project name allowed
- Empty description allowed
- Special characters in descriptions work
- Very long argument names rejected
- Chrome 90+ (primary target)
- Firefox 88+
- Edge 90+
- Safari 14+
- File System Access API or fallback works
- Graph with 100+ nodes remains responsive
- Layout computation completes in reasonable time
- API calls respond within 5 seconds for small graphs
- PNG export produces high-quality image
- No memory leaks after repeated operations
After any code change, verify:
- Core workflows still function (create graph, compute semantics, export)
- No new console errors appear
- Existing features not broken
- Performance not degraded
The Argumentation Designer is licensed under the MIT License.
The MIT License is a permissive free software license that allows:
- Commercial and private use
- Modification and distribution
- Sublicensing
The only requirement is that the license and copyright notice must be included in all copies or substantial portions of the software.
This tool is developed within the FAIR (Future Artificial Intelligence Research) project, specifically under SPOKE 9, which focuses on argumentation and reasoning systems.
Project Details:
- Project Code: PE00000013
- CUP: H23C22000860006
- Program: Piano Nazionale di Ripresa e Resilienza (PNRR)
- Mission: Missione 4 - Istruzione e Ricerca
- Component: Componente 2 - Dalla ricerca all'impresa
- Investment: Investimento 1.3 - Partenariati estesi
- Funding: European Union - NextGenerationEU
Institution: Università della Calabria - DIMES (Dipartimento di Ingegneria Informatica, Modellistica, Elettronica e Sistemistica)
The footer of the application displays this acknowledgment in compliance with funding requirements.
The Argumentation Designer relies on several open-source libraries and frameworks:
Frontend Libraries:
-
Cytoscape.js (v3.33.1) - MIT License
- Graph theory library for visualization and analysis
- https://js.cytoscape.org/
-
cytoscape-node-html-label (v1.2.2) - MIT License
- Plugin for rendering HTML labels inside nodes
- https://github.com/kaluginserg/cytoscape-node-html-label
-
html2canvas - MIT License
- JavaScript HTML renderer for PNG export fallback
- https://html2canvas.hertzen.com/
Backend Libraries:
-
Flask (v2.0+) - BSD-3-Clause License
- Lightweight Python web framework
- https://flask.palletsprojects.com/
-
Clingo (v5.5+) - MIT License
- Answer Set Programming solver for extension-based semantics
- https://potassco.org/clingo/
-
NumPy (v1.21+) - BSD License
- Numerical computing library for gradual semantics
- https://numpy.org/
-
Gunicorn (v20.1+) - MIT License
- Python WSGI HTTP server for production deployment
- https://gunicorn.org/
Infrastructure:
- Apache HTTP Server - Apache License 2.0
- Web server and reverse proxy
- https://httpd.apache.org/
All third-party libraries are used in accordance with their respective licenses. Users of the Argumentation Designer should comply with these licenses when redistributing or modifying the software.
- UI/UX Design: The interface has been designed following the aesthetic principles and component patterns of shadcn/ui.
For a complete list of contributors and the research team behind this project, visit the "People" page at: https://argumentation.dimes.unical.it/people.html
For scientific publications related to this tool and the underlying argumentation framework research, visit: https://argumentation.dimes.unical.it/publications.html
If you use the Argumentation Designer in your research, please cite the FAIR project and reference the tool's website.

