AddFile: Add deleted flags.

scenarios
Pierre-Antoine Rouby 2024-08-27 17:08:40 +02:00
parent 8c99268bb8
commit ac32995e01
5 changed files with 394 additions and 10 deletions

View File

@ -112,6 +112,7 @@ class AddFile(SQLSubModel):
CREATE TABLE additional_files{ext} ( CREATE TABLE additional_files{ext} (
{cls.create_db_add_pamhyr_id()}, {cls.create_db_add_pamhyr_id()},
enabled BOOLEAN NOT NULL, enabled BOOLEAN NOT NULL,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
name TEXT NOT NULL, name TEXT NOT NULL,
path TEXT NOT NULL, path TEXT NOT NULL,
text TEXT NOT NULL, text TEXT NOT NULL,
@ -126,9 +127,9 @@ class AddFile(SQLSubModel):
@classmethod @classmethod
def _db_update(cls, execute, version, data=None): def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".") major, minor, release = version.strip().split(".")
if major == minor == "0":
release = int(release) release = int(release)
if major == minor == "0":
if release < 8: if release < 8:
cls._db_create(execute) cls._db_create(execute)
return True return True
@ -136,6 +137,13 @@ class AddFile(SQLSubModel):
if 8 < release < 13: if 8 < release < 13:
cls._db_update_to_0_0_13(execute, data) cls._db_update_to_0_0_13(execute, data)
if major == "0" and minor == "1":
if release < 2:
execute(
"ALTER TABLE additional_files " +
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
)
return True return True
@classmethod @classmethod
@ -166,7 +174,7 @@ class AddFile(SQLSubModel):
return new return new
table = execute( table = execute(
"SELECT pamhyr_id, enabled, name, path, text, scenario " + "SELECT pamhyr_id, enabled, deleted, name, path, text, scenario " +
"FROM additional_files " + "FROM additional_files " +
f"WHERE scenario = {scenario.id} " + f"WHERE scenario = {scenario.id} " +
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})" f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
@ -177,6 +185,7 @@ class AddFile(SQLSubModel):
id = next(it) id = next(it)
enabled = (next(it) == 1) enabled = (next(it) == 1)
deleted = (next(it) == 1)
name = next(it) name = next(it)
path = next(it) path = next(it)
text = next(it) text = next(it)
@ -186,6 +195,8 @@ class AddFile(SQLSubModel):
id=id, enabled=enabled, name=name, path=path, text=text, id=id, enabled=enabled, name=name, path=path, text=text,
status=data['status'], owner_scenario=owner_scenario status=data['status'], owner_scenario=owner_scenario
) )
if deleted:
f.set_as_deleted()
loaded.add(id) loaded.add(id)
new.append(f) new.append(f)
@ -200,18 +211,17 @@ class AddFile(SQLSubModel):
if not self.must_be_saved(): if not self.must_be_saved():
return True return True
sql = ( execute(
"INSERT INTO " + "INSERT INTO " +
"additional_files(pamhyr_id, enabled, " + "additional_files(pamhyr_id, enabled, deleted, " +
"name, path, text, scenario) " + "name, path, text, scenario) " +
"VALUES (" + "VALUES (" +
f"{self._pamhyr_id}, {self._enabled}, " + f"{self._pamhyr_id}, {self._enabled}, {self.is_deleted()}, " +
f"'{self._db_format(self._name)}', " + f"'{self._db_format(self._name)}', " +
f"'{self._db_format(self._path)}', " + f"'{self._db_format(self._path)}', " +
f"'{self._db_format(self._text)}', " + f"'{self._db_format(self._text)}', " +
f"{self._status.scenario_id}" f"{self._status.scenario_id}"
")" ")"
) )
execute(sql)
return True return True

View File

@ -19,7 +19,7 @@
from tools import trace, timer from tools import trace, timer
from Model.Except import NotImplementedMethodeError from Model.Except import NotImplementedMethodeError
from Model.Tools.PamhyrList import PamhyrModelList from Model.Tools.PamhyrListExt import PamhyrModelList
from Model.AdditionalFile.AddFile import AddFile from Model.AdditionalFile.AddFile import AddFile

View File

@ -162,6 +162,17 @@ class SQLSubModel(PamhyrID):
super(SQLSubModel, self).__init__(id) super(SQLSubModel, self).__init__(id)
self._status = status self._status = status
# Deletion status of the model object. This status MUST be set
# to True if an object that exists in a parent scenario is
# deleted in current scenario.
self._deleted = False
# The 'owner_scenario' is the id of the scenario to which the
# object belongs. This id CAN be different to current
# scenario, but in case of object modification, this id MUST
# be set to current scenario id. (This action is made in
# 'modified' method.)
self._owner_scenario = 0 self._owner_scenario = 0
if owner_scenario == -1: if owner_scenario == -1:
if status is not None: if status is not None:
@ -170,12 +181,49 @@ class SQLSubModel(PamhyrID):
self._owner_scenario = owner_scenario self._owner_scenario = owner_scenario
def must_be_saved(self): def must_be_saved(self):
"""Return True if this object MUST be save in the save file.
Returns:
True if this object MUST be save, otherelse False
"""
return self._owner_scenario == self._status.scenario_id return self._owner_scenario == self._status.scenario_id
def modified(self): def modified(self):
"""Set study status to modified and update the object
owner_scenario to current scenario
Returns:
Nothing
"""
self._owner_scenario = self._status.scenario_id self._owner_scenario = self._status.scenario_id
self._status.modified() self._status.modified()
def is_deleted(self):
"""This object is deleted?
Returns:
True if this object is deleted, otherelse False
"""
return self._deleted
def set_as_deleted(self):
"""Set object deleted flag to True.
Returns:
Nothing
"""
self._deleted = True
self.modified()
def set_as_not_deleted(self):
"""Set object deleted flag to False.
Returns:
Nothing
"""
self._deleted = False
self.modified()
def _db_format(self, value): def _db_format(self, value):
# Replace ''' by '&#39;' to preserve SQL injection # Replace ''' by '&#39;' to preserve SQL injection
if type(value) is str: if type(value) is str:

View File

@ -0,0 +1,324 @@
# PamhyrList.py -- Pamhyr Abstract List object for the Model
# Copyright (C) 2023-2024 INRAE
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-
import logging
from copy import copy
from tools import trace, timer
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Except import NotImplementedMethodeError
logger = logging.getLogger()
class PamhyrModelList(SQLSubModel):
_sub_classes = []
def __init__(self, status=None):
super(PamhyrModelList, self).__init__()
self._status = status
self._lst = []
#######
# SQL #
#######
@classmethod
def _db_create(cls, execute):
return cls._create_submodel(execute)
@classmethod
def _db_update(cls, execute, version, data=None):
return cls._update_submodel(execute, version, data)
@classmethod
def _db_load(cls, execute, data=None):
raise NotImplementedMethodeError(cls, cls._db_load)
def _db_save(self, execute, data=None):
raise NotImplementedMethodeError(self, self._db_save)
################
# MODEL METHOD #
################
@property
def lst(self):
"""Return the PamhyrList as a Python list
Return the PamhyrList as a Python list. This list is the
current list witout deleted object used in the PamhyrList
object. **If you need to modify it, please make a copy.**
Returns: The Python list
"""
return list(
filter(
lambda el: not el.is_deleted(),
self._lst
)
)
def __len__(self):
return len(self.lst)
def index(self, el):
return self.lst.index(el)
def get(self, index):
return self.lst[index]
def set(self, index, new):
self.lst[index] = new
if self._status is not None:
self._status.modified()
def new(self, index):
"""Create new elements and add it to list
Args:
index: The index of new elements
Returns:
The new elements
"""
raise NotImplementedMethodeError(self, self.new)
def insert(self, index, new):
self._lst.insert(index, new)
if self._status is not None:
self._status.modified()
def undelete(self, lst):
"""Delete a list of elements
Args:
lst: The list of elements
Returns:
Nothing
"""
for el in lst:
el.set_as_not_deleted()
def delete(self, lst):
"""Delete a list of elements
Args:
lst: The list of elements
Returns:
Nothing
"""
for el in lst:
el.set_as_deleted()
def delete_i(self, indexes):
"""Delete elements from list of indexes
Args:
indexes: The elements indexes
Returns:
Nothing
"""
lst = list(
map(
lambda x: x[1],
filter(
lambda x: x[0] in indexes,
enumerate(self.lst)
)
)
)
self.delete(lst)
def sort(self, reverse=False, key=None):
self._lst.sort(reverse=reverse, key=key)
if self._status is not None:
self._status.modified()
def move_up(self, index):
if index < len(self._lst):
next = index - 1
lst = self._lst
lst[index], lst[next] = lst[next], lst[index]
if self._status is not None:
self._status.modified()
def move_down(self, index):
if index >= 0:
prev = index + 1
lst = self._lst
lst[index], lst[prev] = lst[prev], lst[index]
if self._status is not None:
self._status.modified()
class PamhyrModelListWithTab(SQLSubModel):
_tabs_list = []
_sub_classes = []
def __init__(self, status=None):
super(PamhyrModelListWithTab, self).__init__()
self._status = status
self._tabs = {}
for tab in self._tabs_list:
self._tabs[tab] = []
#######
# SQL #
#######
@classmethod
def _db_create(cls, execute):
return cls._create_submodel(execute)
@classmethod
def _db_update(cls, execute, version, data=None):
return cls._update_submodel(execute, version, data)
@classmethod
def _db_load(cls, execute, data=None):
raise NotImplementedMethodeError(cls, cls._db_load)
def _db_save(self, execute, data=None):
raise NotImplementedMethodeError(self, self._db_save)
################
# MODEL METHOD #
################
def len(self, lst):
"""Size of tab list
Args:
lst: The tab name
Returns:
The size of the tab list
"""
return len(self._tabs[lst])
def get_tab(self, lst):
"""Get tab list (copy) from name
Args:
lst: The tab name
Returns:
Nothing
"""
return self._tabs[lst].copy()
def get(self, lst, index):
return self._tabs[lst][index]
def set(self, lst, index, new):
self._tabs[lst][index] = new
self._status.modified()
def new(self, lst, index):
"""Create new elements and add it to list
Args:
lst: The tab name
index: The index of new elements
Returns:
The new elements
"""
raise NotImplementedMethodeError(self, self.new)
def insert(self, lst, index, new):
"""Insert element in tab
Args:
lst: The tab name
index: The index of new element
new: The new elements
Returns:
Nothing
"""
self._tabs[lst].insert(index, new)
self._status.modified()
def delete(self, lst, els):
"""Delete elements from specific tab
Args:
lst: The tab name
els: The elements list
Returns:
Nothing
"""
for el in els:
self._tabs[lst].remove(el)
self._status.modified()
def delete_i(self, lst, indexes):
"""Delete elements from specific tab
Args:
lst: The tab name
indexes: The elements indexes
Returns:
Nothing
"""
els = list(
map(
lambda x: x[1],
filter(
lambda x: x[0] in indexes,
enumerate(self._tabs[lst])
)
)
)
self.delete(lst, els)
def sort(self, lst, reverse=False, key=None):
self._tabs[lst].sort(reverse=reverse, key=key)
self._status.modified()
def move_up(self, lst, index):
if index < len(self._tabs[lst]):
next = index - 1
lst = self._tabs[lst]
lst[index], lst[next] = lst[next], lst[index]
self._status.modified()
def move_down(self, lst, index):
if index >= 0:
prev = index + 1
lst = self._tabs[lst]
lst[index], lst[prev] = lst[prev], lst[index]
self._status.modified()

View File

@ -64,7 +64,8 @@ class AddCommand(QUndoCommand):
if self._new is None: if self._new is None:
self._new = self._files.new(self._row) self._new = self._files.new(self._row)
else: else:
self._files.insert(self._row, self._new) self._files.undelete([self._new])
# self._files.insert(self._row, self._new)
class DelCommand(QUndoCommand): class DelCommand(QUndoCommand):
@ -76,7 +77,8 @@ class DelCommand(QUndoCommand):
self._old = self._files.get(row) self._old = self._files.get(row)
def undo(self): def undo(self):
self._files.insert(self._row, self._old) self._files.undelete([self._old])
# self._files.insert(self._row, self._old)
def redo(self): def redo(self):
self._files.delete_i([self._row]) self._files.delete_i([self._row])