# This file is part of the Go-Smart Simulation Architecture (GSSA).
# Go-Smart is an EU-FP7 project, funded by the European Commission.
#
# Copyright (C) 2013- NUMA Engineering Ltd. (see AUTHORS file)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sqlite3
import os
import logging
logger = logging.getLogger(__name__)
from .definition import GoSmartSimulationDefinition
# SQLite database for storing information about active databases
[docs]class SQLiteSimulationDatabase:
def __init__(self, database):
# If we do not currently have a database file, we should make one
should_create = not os.path.exists(database)
self._db = sqlite3.connect(database)
self._db.row_factory = sqlite3.Row
if should_create:
self.create()
[docs] def updateValidation(self, guid, validation_xml):
"""Add the validation XML string to the simulation row."""
cursor = self._db.cursor()
cursor.execute('''
UPDATE simulations
SET validation=:validation
WHERE guid=:guid
''', {'guid': guid, 'validation': validation_xml})
self._db.commit()
[docs] def getValidation(self, guid):
"""Return just the validation XML string for a simulation."""
cursor = self._db.cursor()
cursor.execute('''
SELECT validation
FROM simulations
WHERE guid=? AND deleted=0
''', guid)
try:
simulation_row = cursor.fetchone()
except Exception:
return None
validation = simulation_row[0]
return validation
[docs] def setStatus(self, guid, exit_code, status, percentage, timestamp):
"""Update the status of a simulation in the database."""
cursor = self._db.cursor()
cursor.execute('''
UPDATE simulations
SET exit_code=:exit_code, status=:status, percentage=:percentage, timestamp=:timestamp
WHERE guid=:guid
''', {"guid": guid, "status": status, "percentage": percentage, "exit_code": exit_code, "timestamp": timestamp})
self._db.commit()
[docs] def getStatusAndValidation(self, guid):
"""Return both status and validation."""
cursor = self._db.cursor()
cursor.execute('''
SELECT status, percentage, exit_code, timestamp, validation
FROM simulations
WHERE guid=? AND deleted=0
''', guid)
try:
simulation_row = cursor.fetchone()
except Exception:
return None
status, percentage, exit_code, timestamp, validation = simulation_row
return percentage, status, exit_code, timestamp, validation
[docs] def create(self):
"""Set up the database."""
cursor = self._db.cursor()
cursor.execute('''
CREATE TABLE simulations(
id INTEGER PRIMARY KEY,
guid TEXT UNIQUE,
directory TEXT,
exit_code TEXT NULLABLE DEFAULT NULL,
status TEXT,
percentage REAL,
timestamp REAL,
validation TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted TINYINT DEFAULT 0
)
''')
self._db.commit()
[docs] def addOrUpdate(self, simulation):
"""Update the simulation row or add a new one if not already here."""
try:
cursor = self._db.cursor()
cursor.execute('''
INSERT OR REPLACE INTO simulations(guid, directory)
VALUES(:guid,:directory)
''', {"guid": simulation.get_guid(), "directory": simulation.get_dir()})
self._db.commit()
except Exception:
logging.exception("Problem inserting data into simulations")
[docs] def markAllOld(self):
"""Mark simulations as exited if still appearing to run - usually on server start-up."""
cursor = self._db.cursor()
cursor.execute('''
UPDATE simulations
SET status=('Unfinished (' || percentage || '%)'), percentage=0, exit_code='E_UNKNOWN'
WHERE percentage IS NOT NULL AND percentage < 100
''')
self._db.commit()
[docs] def active_count(self):
"""Check how many simulations are still marked IN_PROGRESS."""
cursor = self._db.cursor()
cursor.execute('''
SELECT COUNT(id) as active
FROM simulations
WHERE status="IN_PROGRESS"
''')
return cursor.fetchone()['active']
[docs] def all(self):
cursor = self._db.cursor()
cursor.execute('''
SELECT *
FROM simulations
''')
simulations = cursor.fetchall()
return simulations
[docs] def search(self, guid_start):
cursor = self._db.cursor()
guid = str(guid_start) + '%'
cursor.execute('''
SELECT *
FROM simulations
WHERE guid LIKE :guid AND deleted=0
''', {'guid': guid})
try:
simulation_rows = cursor.fetchall()
except Exception:
return None
# Simulations should not be added to the database until they are finalized
def buildsim(s):
d = GoSmartSimulationDefinition(s['guid'], None, s['directory'], None, finalized=True)
d._status = {'percentage': s['percentage'], 'message': s['status'], 'timestamp': s['timestamp']}
d.set_exit_status(s['exit_code'], s['status'])
return d
simulations = {s['guid']: buildsim(s) for s in simulation_rows if os.path.exists(s['directory'])}
return simulations
[docs] def retrieve(self, guid):
"""Get a simulation by the client's GUID."""
if len(guid) < 32:
return self.search(guid)
cursor = self._db.cursor()
cursor.execute('''
SELECT *
FROM simulations
WHERE guid=:guid AND deleted=0
''', {'guid': guid})
try:
simulation_row = cursor.fetchone()
except Exception:
return None
if not simulation_row:
return None
directory = simulation_row['directory']
if not os.path.exists(directory):
return None
# Simulations should not be added to the database until they are finalized
return GoSmartSimulationDefinition(guid, None, directory, None, finalized=True)
[docs] def delete(self, simulation, soft=True):
"""Remove a simulation from the database.
Args:
simulation (str): GUID of the simulation.
soft (Optional[bool]): do a soft delete.
"""
cursor = self._db.cursor()
if soft:
cursor.execute('''
UPDATE simulations
SET deleted=1
WHERE guid=?
''', simulation.get_guid())
else:
cursor.execute('''
DELETE FROM simulations
WHERE guid=?
''', simulation.get_guid())
def __del__(self):
# Tidy up before we leave
self._db.close()