diff --git a/.env.example b/.env.example
index cb97c80..ed60e95 100644
--- a/.env.example
+++ b/.env.example
@@ -1,17 +1,16 @@
DOMAIN=localhost
+DEMO=true
# note that with DEBUG=true, logs are more verbose (include tracebacks)
DEBUG=true
-DEMO=true
+ALLOWED_HOSTS=localhost,localhost:8000,127.0.0.1,
STATIC_ROOT=/tmp/static/
MEDIA_ROOT=/tmp/media/
-ALLOWED_HOSTS=localhost,localhost:8000,127.0.0.1,
-DOMAIN=localhost
EMAIL_HOST="mail.example.org"
EMAIL_HOST_USER="fillme_noreply"
EMAIL_HOST_PASSWORD="fillme_passwd"
EMAIL_PORT=587
-EMAIL_USE_TLS=True
+EMAIL_USE_TLS=true
EMAIL_BACKEND="django.core.mail.backends.smtp.EmailBackend"
EMAIL_FILE_PATH="/tmp/app-messages"
ENABLE_EMAIL=false
diff --git a/README.md b/README.md
index 375cd85..6a39255 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,123 @@
-# INSTALACIÓN:
+# Device Hub
-La instalación es muy estándar
+DeviceHub is an IT Asset Management System focused on reusing devices, created under the [eReuse.org](https://www.ereuse.org) project.
+## Overview
+
+DeviceHub aims to:
+
+- Provide a common IT Asset Management platform for donors, receivers, and IT professionals.
+- Automatically collect, analyze, and share device metadata while ensuring privacy and traceability.
+- Integrate with existing IT Asset Management Systems.
+- Operate in a decentralized manner.
+
+DeviceHub primarily works with three types of objects:
+
+1. **Devices**: Including computers, smartphones, and their components.
+2. **Events**: Actions performed on devices (e.g., Repair, Allocate).
+3. **Accounts**: Users who perform events on devices.
+
+## Installation
+
+Assuming a host with debian stable
+
+### Quickstart
+
+For a quick start with dummy data in localhost, DeviceHub can be run directly with docker. To do so, from the root of the project run:
+
+```bash
+./docker-reset.sh
```
+
+Note that everytime you perform the `docker-reset.sh` script, all data is lost.
+
+Also there is a demo running in http://demo.ereuse.org/. The token for accessing the instance will be always: `token=5018dd65-9abd-4a62-8896-80f34ac66150`, but the instance will be reset every day at 4 am.
+
+For production needs, review and change .env file properly
+
+## Running from baremetal
+
+### Prerequisites
+
+- Python 3.10
+- pip
+- virtualenv
+
+Specially when developing, is quite convenient to run DeviceHub from a virtual environment. To start with this deployment, create a virtual environment to isolate our project dependencies:
+
+```bash
python -m venv env
-source env/bin/actevate
-python install -r requirements.txt
+source env/bin/activate
+pip install -r requirements.txt
```
-## IMPORTANT EXTERNAL DEPENDENCIES
+### System Dependencies
-Para arrancarlo es necesario tener el paquete `xapian-bindings` en tu ordenador. No se instala mediante `pip`, así que depende de cada [sistema operativo](https://xapian.org/download).
+#### Xapian
-Luego solo necesitas:
+Now, install the xapian dependencies (xapian library and python bindings)
+```bash
+sudo apt-get install python3-xapian libxapian-dev
```
-./manage.py migrate
-./manage.py runserver
+
+Allow the virtual environment to use system-installed packages:
+
+```bash
+export PYTHONPATH="${PYTHONPATH}:/usr/lib/python3/dist-packages"
```
+
+#### Environment Variables
+
+Now, configure the environment variables. For this, we will expand a `.env` file. You can use the following content as an example:
+
+```source
+STATIC_ROOT=/tmp/static/
+MEDIA_ROOT=/tmp/media/
+ALLOWED_HOSTS=localhost,localhost:8000,127.0.0.1,
+DOMAIN=localhost
+DEBUG=True
+```
+
+Now, expand the environment variables:
+
+```bash
+source .env
+```
+
+### Migrations
+
+Now, apply migrations
+
+```bash
+python manage.py makemigrations
+python manage.py migrate
+```
+
+Also, we can add some dummy data into the database to play along:
+
+```bash
+python manage.py add_institution Pangea
+python manage.py add_user Pangea user@example.org 1234
+python manage.py up_snapshots example/snapshots/ user@example.org
+```
+
+### Run DeviceHub
+
+Finally, we can run the DeviceHub service by running:
+
+```bash
+python manage.py runserver
+```
+
+### Clean up
+
+To clean up the deployment and start fresh, just delete Django's database:
+
+```bash
+rm db/*
+```
+
+## License
+
+DeviceHub is released under the [GNU Affero General Public License v3.0](LICENSE).
diff --git a/device/templates/details.html b/device/templates/details.html
index 6af4e86..6fdefb9 100644
--- a/device/templates/details.html
+++ b/device/templates/details.html
@@ -61,7 +61,7 @@
{{ object.type }}
- {% if object.is_websnapshot %}
+ {% if object.is_websnapshot and object.last_user_evidence %}
{% for k, v in object.last_user_evidence %}
{{ k }}
diff --git a/docker-reset.sh b/docker-reset.sh
index bf14db2..9dc0032 100755
--- a/docker-reset.sh
+++ b/docker-reset.sh
@@ -14,6 +14,11 @@ main() {
if [ "${DETACH:-}" ]; then
detach_arg='-d'
fi
+
+ if [ ! -f .env ]; then
+ cp -v .env.example .env
+ echo "WARNING: .env was not there, .env.example was copied, this only happens once"
+ fi
# remove old database
sudo rm -vfr ./db/*
docker compose down -v
diff --git a/evidence/forms.py b/evidence/forms.py
index dab7a60..83d9e6b 100644
--- a/evidence/forms.py
+++ b/evidence/forms.py
@@ -15,7 +15,7 @@ from utils.save_snapshots import move_json, save_in_disk
class UploadForm(forms.Form):
evidence_file = MultipleFileField(label=_("File"))
- def clean(self):
+ def clean_evidence_file(self):
self.evidences = []
data = self.cleaned_data.get('evidence_file')
if not data:
@@ -33,13 +33,20 @@ class UploadForm(forms.Form):
exist_annotation = Annotation.objects.filter(
uuid=file_json['uuid']
).first()
-
+
if exist_annotation:
- raise ValidationError("error: {} exist".format(file_name))
-
- except Exception:
- raise ValidationError("error in: {}".format(file_name))
-
+ raise ValidationError(
+ _("The snapshot already exists"),
+ code="duplicate_snapshot",
+ )
+
+ #Catch any error and display it as Validation Error so the Form handles it
+ except Exception as e:
+ raise ValidationError(
+ _("Error on '%(file_name)s': %(error)s"),
+ code="error",
+ params={"file_name": file_name, "error": getattr(e, 'message', str(e))},
+ )
self.evidences.append((file_name, file_json))
return True
@@ -123,7 +130,15 @@ class ImportForm(forms.Form):
data = self.cleaned_data["file_import"]
self.file_name = data.name
- df = pd.read_excel(data)
+
+ try:
+ df = pd.read_excel(data)
+ except Exception as e:
+ raise ValidationError(
+ _("Error on '%(file_name)s': Invalid File"),
+ params={"file_name": self.file_name}
+ )
+
df.fillna('', inplace=True)
data_pd = df.to_dict(orient='index')
diff --git a/evidence/management/commands/up_snapshots.py b/evidence/management/commands/up_snapshots.py
index c987d76..36f5c28 100644
--- a/evidence/management/commands/up_snapshots.py
+++ b/evidence/management/commands/up_snapshots.py
@@ -47,17 +47,22 @@ class Command(BaseCommand):
self.open(filepath)
def open(self, filepath):
- with open(filepath, 'r') as file:
- content = json.loads(file.read())
- path_name = save_in_disk(content, self.user.institution.name)
- self.snapshots.append((content, path_name))
+ try:
+ with open(filepath, 'r') as file:
+ content = json.loads(file.read())
+ path_name = save_in_disk(content, self.user.institution.name)
+
+ self.snapshots.append((content, path_name))
+
+ except Exception as e:
+ logger.error("Could not open file %s: %s", filepath, e)
def parsing(self):
for s, p in self.snapshots:
try:
self.devices.append(Build(s, self.user))
move_json(p, self.user.institution.name)
- except Exception as err:
+ except Exception as e:
snapshot_id = s.get("uuid", "")
- txt = "Could not parse snapshot: %s"
- logger.error(txt, snapshot_id)
+ txt = "Could not parse snapshot %s: %s"
+ logger.error(txt, snapshot_id, e)
diff --git a/evidence/parse.py b/evidence/parse.py
index 3ec476b..fd68e06 100644
--- a/evidence/parse.py
+++ b/evidence/parse.py
@@ -4,6 +4,7 @@ import logging
from dmidecode import DMIParse
from json_repair import repair_json
+from evidence.parse_details import get_lshw_child
from evidence.models import Annotation
from evidence.xapian import index
@@ -12,16 +13,7 @@ from utils.constants import CHASSIS_DH
logger = logging.getLogger('django')
-
-def get_network_cards(child, nets):
- if child['id'] == 'network' and "PCI:" in child.get("businfo"):
- nets.append(child)
- if child.get('children'):
- [get_network_cards(x, nets) for x in child['children']]
-
-
def get_mac(lshw):
- nets = []
try:
if type(lshw) is dict:
hw = lshw
@@ -30,18 +22,16 @@ def get_mac(lshw):
except json.decoder.JSONDecodeError:
hw = json.loads(repair_json(lshw))
- try:
- get_network_cards(hw, nets)
- except Exception as ss:
- logger.warning("%s", ss)
- return
+ nets = []
+ get_lshw_child(hw, nets, 'network')
nets_sorted = sorted(nets, key=lambda x: x['businfo'])
- # This funcion get the network card integrated in motherboard
- # integrate = [x for x in nets if "pci@0000:00:" in x.get('businfo', '')]
if nets_sorted:
- return nets_sorted[0]['serial']
+ mac = nets_sorted[0]['serial']
+ logger.debug("The snapshot has the following MAC: %s" , mac)
+ return mac
+
class Build:
diff --git a/evidence/templates/upload.html b/evidence/templates/upload.html
index 056cf55..6337b3e 100644
--- a/evidence/templates/upload.html
+++ b/evidence/templates/upload.html
@@ -8,23 +8,21 @@
+
+
+
{% load django_bootstrap5 %}