You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
220 lines
6.7 KiB
220 lines
6.7 KiB
import std/[tables, strutils, sugar, hashes, algorithm, os]
|
|
from std/sequtils import toSeq
|
|
import tiny_sqlite
|
|
import "../shared"
|
|
|
|
# We create a custom type for representing the ID of a routine.
|
|
# This way Nim's type system automatically prevents us from using it as a regular integer and
|
|
# from confusing it with the ID of another entity.
|
|
|
|
type RoutineId* = distinct int64
|
|
|
|
proc `==`*(a, b: RoutineId): bool {.borrow.}
|
|
proc hash*(id: RoutineId): Hash {.borrow.}
|
|
proc `$`*(id: RoutineId): string =
|
|
result = "routine" & $int64(id)
|
|
|
|
type Routine* = object
|
|
id*: RoutineId
|
|
name*: string
|
|
selected*: bool
|
|
#selByDef == selected by default.
|
|
selByDef*: bool
|
|
sources*: seq[string]
|
|
destinations*: seq[string]
|
|
|
|
proc stringToSeq(commaSepString: string):seq[string] =
|
|
#return toSeq(commaSepString.split(','))
|
|
var newSeq: seq[string]
|
|
if hasCommas(commaSepString) == true:
|
|
newSeq = toSeq(commaSepString.split(','))
|
|
else:
|
|
newSeq = @[commaSepString]
|
|
|
|
return newSeq
|
|
|
|
proc seqToString(strSeq: seq[string]):string =
|
|
#return strSeq.join(",")
|
|
var newStr: string
|
|
if strSeq.len >= 1:
|
|
newStr = strSeq.join(",")
|
|
else:
|
|
newStr = ""
|
|
|
|
return newStr
|
|
|
|
proc locationsExist*(routine: Routine): bool =
|
|
var logMsg: string
|
|
var exists: seq[int]
|
|
for source in routine.sources:
|
|
if dirExists(source) or fileExists(source):
|
|
exists.add(1)
|
|
else:
|
|
logMsg = "(" & nowDT & ")" & " Source not found: " & source
|
|
writeErrorToLog(logMsg)
|
|
exists.add(0)
|
|
|
|
for dest in routine.destinations:
|
|
if dirExists(dest) or fileExists(dest):
|
|
exists.add(1)
|
|
else:
|
|
logMsg = "(" & nowDT & ")" & " Destination not found: " & dest
|
|
writeErrorToLog(logMsg)
|
|
exists.add(0)
|
|
|
|
if 0 in exists:
|
|
return false
|
|
else:
|
|
return true
|
|
|
|
#[proc matches*(routine: Routine, filter: string): bool =
|
|
## Checks if the routine matches the given filter.
|
|
## This function is used to search the list of routines.
|
|
filter.toLowerAscii() in toLowerAscii(routine.name)]#
|
|
|
|
type RoutineModel* = ref object
|
|
## Model for storing all routines. We model this as a ref object, so that changes
|
|
## made by any widget are also known to all other widgets that use the model.
|
|
|
|
db: DbConn
|
|
routines*: Table[RoutineId, Routine]
|
|
|
|
proc cmpRoutines(a, b: Routine): int =
|
|
cmp(a.name, b.name)
|
|
|
|
|
|
#this is our stand-in for the search proc. returns a seq so we can iterate in routine_list.
|
|
proc routineSeq*(model: RoutineModel): seq[Routine] =
|
|
var routineSeq: seq[Routine]
|
|
## Returns a seq of all routines. "id" is necessary to loop through table.
|
|
for id, routine in model.routines:
|
|
routineSeq.add(routine)
|
|
|
|
routineSeq.sort(cmpRoutines, Ascending)
|
|
|
|
#echo routineSeq
|
|
|
|
return routineSeq
|
|
|
|
let homePath = expandTilde("~")
|
|
|
|
proc newRoutineModel*(path: string = homePath & "/.ez-bkup/ez-bkup.sqlite"): RoutineModel =
|
|
## Load a RoutineModel from a database
|
|
|
|
var db: DbConn
|
|
try:
|
|
db = openDatabase(path)
|
|
except:
|
|
writeErrorToLog("Unable to open and/or create sqlite3 database. Please make sure sqlite3 is installed.")
|
|
db = openDatabase("")
|
|
|
|
# Create the Routine table
|
|
db.exec("""
|
|
CREATE TABLE IF NOT EXISTS Routine(
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT,
|
|
selByDef INTEGER,
|
|
sources TEXT,
|
|
destinations TEXT
|
|
)
|
|
""")
|
|
|
|
# Load all existing routines into RoutineModel.routines
|
|
var routines = initTable[RoutineId, Routine]()
|
|
for row in db.iterate("SELECT id, name, selByDef, sources, destinations FROM Routine"):
|
|
let (id, name, selByDef, sources, destinations) = row.unpack((RoutineId, string, bool, string, string))
|
|
|
|
var sourcesSeq: seq[string]
|
|
#no sources exist in DB.
|
|
if sources == "":
|
|
sourcesSeq = @[]
|
|
else:
|
|
#convert "sources" comma-separated string to seq[string].
|
|
sourcesSeq = stringToSeq(sources)
|
|
|
|
var destinationsSeq: seq[string]
|
|
#no destinations exist in DB.
|
|
if destinations == "":
|
|
destinationsSeq = @[]
|
|
else:
|
|
#convert "destinations" comma-separated string to seq[string].
|
|
destinationsSeq = stringToSeq(destinations)
|
|
|
|
routines[id] = Routine(id: id, name: name, selByDef: selByDef, sources: sourcesSeq, destinations: destinationsSeq)
|
|
|
|
#echo "newRoutineModel:"
|
|
#echo routines
|
|
|
|
result = RoutineModel(db: db, routines: routines)
|
|
|
|
proc add*(model: RoutineModel, routine: Routine) =
|
|
## Adds a new routine to the model
|
|
|
|
#convert routine.sources seq[string] to a comma-separated string to store as TEXT in sqlite.
|
|
let stringSources = seqToString(routine.sources)
|
|
|
|
#convert routine.destinations seq[string] to a comma-separated string to store as TEXT in sqlite.
|
|
let stringDestinations = seqToString(routine.destinations)
|
|
|
|
# Insert new routine into database
|
|
model.db.exec(
|
|
"INSERT INTO Routine (name, selByDef, sources, destinations) VALUES (?, ?, ?, ?)",
|
|
routine.name, routine.selByDef, stringSources, stringDestinations
|
|
)
|
|
|
|
# Insert new routine into RoutineModel.routines
|
|
let id = RoutineId(model.db.lastInsertRowId)
|
|
model.routines[id] = routine.dup(id = id)
|
|
|
|
proc update*(model: RoutineModel, routine: Routine) =
|
|
## Updates an existing routine. Routines are compared by their ID.
|
|
|
|
#convert routine.sources seq[string] to a comma-separated string to store as TEXT in sqlite.
|
|
let stringSources = seqToString(routine.sources)
|
|
|
|
#convert routine.destinations seq[string] to a comma-separated string to store as TEXT in sqlite.
|
|
let stringDestinations = seqToString(routine.destinations)
|
|
|
|
# Update routine in database
|
|
model.db.exec(
|
|
"UPDATE Routine SET name = ?, selByDef = ?, sources = ?, destinations = ? WHERE id = ?",
|
|
routine.name, routine.selByDef, stringSources, stringDestinations, routine.id
|
|
)
|
|
|
|
# Update RoutineModel.routines
|
|
model.routines[routine.id] = routine
|
|
|
|
#[proc search*(model: UserModel, filter: string): seq[User] {.locks: 0.} =
|
|
## Returns a seq of all users that match the given filter.
|
|
for id, user in model.users:
|
|
if user.matches(filter):
|
|
result.add(user)
|
|
|
|
result.sort((a, b: User) => cmp(a.lastName, b.lastName))]#
|
|
|
|
proc delete*(model: RoutineModel, id: RoutineId) =
|
|
## Deletes the routine with the given ID
|
|
|
|
# Delete routine from database
|
|
model.db.exec("DELETE FROM Routine WHERE id = ?", id)
|
|
|
|
# Update RoutineModel.routines
|
|
model.routines.del(id)
|
|
|
|
proc clear*(model: RoutineModel) =
|
|
## Deletes all routines
|
|
|
|
# Delete all routines from database
|
|
model.db.exec("DELETE FROM Routine")
|
|
|
|
# Update RoutineModel.routines
|
|
model.routines.clear()
|
|
|
|
#preload the selected seq with routines that have selByDef == true.
|
|
proc selectedPreload*(): seq[RoutineId] =
|
|
var selected: seq[RoutineId]
|
|
let model = newRoutineModel(databasePath)
|
|
for routine in model.routineSeq():
|
|
if routine.selByDef == true:
|
|
selected.add(routine.id)
|
|
return selected |