#region Disclaimer / License // Copyright (C) 2011, Kenneth Bergeron, IAP Worldwide Services, Inc // NOAA :: National Marine Fisheries Service // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library 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 // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // #endregion using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Duplicati.Scheduler.Data { /// /// Provides some tools used with the history dataset /// public partial class HistoryDataSet { /// /// The name of the XML file (no path) /// public const string DefaultName = "History.xml"; /// /// The XML file path without the file name /// public static string DefaultPath() { return GetAppData(DefaultName); } /// /// The application path and filename, making sure it exists /// public static string GetAppData(string aFileName) { string Result = Utility.User.GetApplicationDirectory(@"Duplicati"); if (!System.IO.Directory.Exists(Result)) System.IO.Directory.CreateDirectory(Result); return System.IO.Path.Combine(Result, aFileName); } /// /// Load the database from the deefault XML file /// /// null if OK, else the exception public Exception Load() { return Load(string.Empty); } /// /// Load the database from a file (if empty, use the default) /// /// File to load or default if null or empty /// null if OK, else the exception public Exception Load(string aFullPath) { if (string.IsNullOrEmpty(aFullPath)) aFullPath = DefaultPath(); if (!System.IO.File.Exists(aFullPath)) return new Exception("File not found: " + aFullPath); this.Clear(); Exception Result = null; try { this.ReadXml(aFullPath); } catch (Exception Ex) { Result = Ex; } return Result; } /// /// Saves the dataset to the default XML /// /// null if OK, else the exception public Exception Save() { return Save(GetAppData(DefaultName)); } /// /// Saves the dataset to the passed file /// /// XML file to load /// null if OK, else the exception public Exception Save(string aFullPath) { Exception Result = null; try { this.WriteXml(aFullPath); } catch (Exception Ex) { Result = Ex; } return Result; } /// /// Tools for the data table /// public partial class HistoryDataTable { /// /// Get all rows for a job /// /// Job to load /// Rows or empty if none public HistoryRow[] GetHistory(string aJob) { return this.Where(qR => qR.Name.Equals(aJob)).ToArray(); } /// /// Gives all rows for a job older than a time span /// /// Job to load /// TimeSpan older than which rows will not be loaded /// public HistoryRow[] GetHistory(string aJob, TimeSpan aYoungerThan) { DateTime Later = DateTime.Now - aYoungerThan; return GetHistory(aJob).Where(qR => qR.ActionDate > Later).ToArray(); } /// /// Returns the last FULL backup for a job or null /// /// Job to load /// the last FULL backup for a job or null public DateTime LastFull(string aJob) { return GetHistory(aJob).Where(qF => qF.Full && qF.Success).Select(qD => qD.ActionDate).DefaultIfEmpty(DateTime.MinValue).Max(); } /// /// Returns the number of runs since the last full backup /// /// Job to load /// The number of runs since the last full backup public int NumberSinceLastFull(string aJob) { DateTime Last = LastFull(aJob); if (Last == DateTime.MinValue) return 0; return NumberSinceLastFull(aJob, Last); } /// /// Returns the number of incremntal runs since a date /// /// Job to run /// Date /// The number of incremntal runs since a date public int NumberSinceLastFull(string aJob, DateTime aLastFull) { return GetHistory(aJob).Where(qF => !qF.Full && qF.Success && qF.ActionDate > aLastFull).Count(); } /// /// Deletes all rows for a job /// /// public void DeleteJob(string aJob) { foreach (HistoryRow Row in GetHistory(aJob)) Row.Delete(); } } /// /// Data row tools /// public partial class HistoryRow { /// /// Update a stats row /// /// Duplicati output (Result) string public void UpdateStats(string aOutput) { StatsRow[] Stats = this.GetStatsRows(); StatsRow sRow = null; if (Stats == null || Stats.Length == 0) sRow = ((StatsDataTable)this.tableHistory.ChildRelations["History_Stats"].ChildTable).AddStatsRow(this, this.ActionDate, 0, 0, 0, 0, 0L, 0L, 0L, 0, 0, 0, this.ActionDate, this.ActionDate, string.Empty, 0, 0, 0, string.Empty, null); else sRow = Stats[0]; // Parse the stats out of the results foreach (string Line in aOutput.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)) { int Colon = Line.IndexOf(':'); if (Colon > 0) { string Field = Line.Substring(0, Colon).Trim(); string Value = Line.Substring(Colon + 1).Trim(); if (sRow.Table.Columns.Contains(Field)) sRow[Field] = System.Convert.ChangeType(Value, sRow.Table.Columns[Field].DataType); } } } /// /// Update a History row /// /// Action done /// Was it full /// OK /// Status /// Checksun used /// Module used public void Update(string aAction, bool aFull, bool aSuccess, string aStatus, byte[] aChecksum, string aCheckMod) { this.Action = aAction; this.Full = aFull; this.Success = aSuccess; this.CheckMod = aCheckMod; this.Checksum = aChecksum; this.UpdateStats(aStatus); } /// /// Delete a history row /// public new void Delete() { foreach (StatsRow sRow in from StatsRow qS in this.GetStatsRows() select qS) sRow.Delete(); base.Delete(); } } } }