banshee r3025 - in trunk/banshee: . src/Core/Banshee.Services/Banshee.Collection.Database src/Core/Banshee.Services/Banshee.Playlist src/Core/Banshee.Services/Banshee.SmartPlaylist src/Core/Banshee.Services/Banshee.Sources src/Core/Hyena/Hyena.Data.Query
- From: gburt svn gnome org
- To: svn-commits-list gnome org
- Subject: banshee r3025 - in trunk/banshee: . src/Core/Banshee.Services/Banshee.Collection.Database src/Core/Banshee.Services/Banshee.Playlist src/Core/Banshee.Services/Banshee.SmartPlaylist src/Core/Banshee.Services/Banshee.Sources src/Core/Hyena/Hyena.Data.Query
- Date: Fri, 25 Jan 2008 23:37:29 +0000 (GMT)
Author: gburt
Date: Fri Jan 25 23:37:29 2008
New Revision: 3025
URL: http://svn.gnome.org/viewvc/banshee?rev=3025&view=rev
Log:
2008-01-25 Gabriel Burt <gabriel burt gmail com>
Smart playlists are migrated and saved now. They should be functional.
* src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs:
Add playlistid and smartplaylistid fields for smart playlist migration.
* src/Core/Banshee.Services/Banshee.Playlist/AbstractPlaylistSource.cs:
* src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs: Call DbId
setter from subclasses, not in base ctor.
* src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs: Improve to
actually be able to migrate the vast majority of smart playlists
including relative date queries and between queries.
* src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistCore.cs:
Remove debug output.
* src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistSource.cs:
Don't allow removing tracks, and change to revolve about Hyena.Data.Query
query tree instead of a SQL condition string.
* src/Core/Banshee.Services/Banshee.Sources/DatabaseSource.cs: Don't allow
overriding of Reload method, since it's really RateLimitedReload
subclasses want to override.
* src/Core/Hyena/Hyena.Data.Query/DateQueryValue.cs: Actually generate
relative date queries, and expose relevant properties.
* src/Core/Hyena/Hyena.Data.Query/QueryField.cs: For StringQueryValue
fields that don't have a custom format, automatically match both literally
and against both the column and value lowercased.
* src/Core/Hyena/Hyena.Data.Query/QueryNode.cs: Add option to print pretty
XML (indented).
* src/Core/Hyena/Hyena.Data.Query/QueryOperator.cs: Add a Dual property
that flips < and > characters.
* src/Core/Hyena/Hyena.Data.Query/XmlQueryParser.cs: Add static
convenience method for parsing.
Modified:
trunk/banshee/ChangeLog
trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs
trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/AbstractPlaylistSource.cs
trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs
trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs
trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistCore.cs
trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistSource.cs
trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/DatabaseSource.cs
trunk/banshee/src/Core/Hyena/Hyena.Data.Query/DateQueryValue.cs
trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryField.cs
trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryNode.cs
trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryOperator.cs
trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryTermNode.cs
trunk/banshee/src/Core/Hyena/Hyena.Data.Query/XmlQueryParser.cs
Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs (original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs Fri Jan 25 23:37:29 2008
@@ -384,11 +384,11 @@
}
public override QueryField ArtistField {
- get { return field_set.Fields [0]; }
+ get { return field_set["artist"]; }
}
public override QueryField AlbumField {
- get { return field_set.Fields [1]; }
+ get { return field_set["album"]; }
}
protected static QueryFieldSet field_set = new QueryFieldSet (
@@ -459,7 +459,6 @@
"type", "mimetype", "format", "ext", "mime"
),
new QueryField (
- // (strftime("%s", current_timestamp) - DateAddedStamp + 3600)
"lastplayed", Catalog.GetString ("Last Played Date"), "CoreTracks.LastPlayedStamp", typeof(DateQueryValue),
// Translators: These are unique search fields. Please, no spaces. Blank ok.
Catalog.GetString ("lastplayed"), Catalog.GetString ("played"), Catalog.GetString ("playedon"),
@@ -470,15 +469,17 @@
// Translators: These are unique search fields. Please, no spaces. Blank ok.
Catalog.GetString ("added"), Catalog.GetString ("imported"), Catalog.GetString ("addedon"), Catalog.GetString ("dateadded"), Catalog.GetString ("importedon"),
"added", "imported", "addedon", "dateadded", "importedon"
- )/*,
+ ),
new QueryField (
- "playlist", Catalog.GetString ("Playlist"),
- "CoreTracks.TrackID IN (SELECT TrackID FROM CorePlaylistEntries WHERE PlaylistID {0})",
- QueryFieldType.Numeric,
- // Translators: These are unique search fields. Please, no spaces. Blank ok.
- Catalog.GetString ("duration"), Catalog.GetString ("length"), Catalog.GetString ("time"),
- "duration", "length", "time"
- )*/
+ "playlistid", Catalog.GetString ("Playlist"),
+ "CoreTracks.TrackID {2} IN (SELECT TrackID FROM CorePlaylistEntries WHERE PlaylistID = {1})", typeof(IntegerQueryValue),
+ "playlistid", "playlist"
+ ),
+ new QueryField (
+ "smartplaylistid", Catalog.GetString ("Smart Playlist"),
+ "CoreTracks.TrackID {2} IN (SELECT TrackID FROM CoreSmartPlaylistEntries WHERE SmartPlaylistID = {1})", typeof(IntegerQueryValue),
+ "smartplaylistid", "smartplaylist"
+ )
);
}
}
Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/AbstractPlaylistSource.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/AbstractPlaylistSource.cs (original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/AbstractPlaylistSource.cs Fri Jan 25 23:37:29 2008
@@ -74,10 +74,8 @@
get { return dbid; }
protected set {
if (value == null) {
- Console.WriteLine ("intializing abstract playlist, but dbid = null!");
return;
}
- Console.WriteLine ("intializing abstract playlist");
dbid = value;
track_model.JoinFragment = TrackJoin;
track_model.Condition = String.Format (TrackCondition, dbid);
@@ -92,7 +90,6 @@
public AbstractPlaylistSource (string generic_name, string name, int? dbid, int sortColumn, int sortType) : base (generic_name, name, Convert.ToString (dbid), 500)
{
Properties.SetString ("IconName", IconName);
- DbId = dbid;
}
public override void Rename (string newName)
@@ -103,7 +100,7 @@
public virtual void Save ()
{
- if (dbid == null || dbid < 0)
+ if (dbid == null || dbid <= 0)
Create ();
else
Update ();
Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs (original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs Fri Jan 25 23:37:29 2008
@@ -99,6 +99,7 @@
{
Properties.SetString ("RemoveTracksActionLabel", Catalog.GetString ("Remove From Playlist"));
Properties.SetString ("UnmapSourceActionLabel", Catalog.GetString ("Delete Playlist"));
+ DbId = dbid;
}
#endregion
Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs (original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs Fri Jan 25 23:37:29 2008
@@ -28,14 +28,17 @@
//
using System;
+using System.Data;
using System.Text.RegularExpressions;
using System.Collections;
using System.Collections.Generic;
using Hyena.Data;
using Hyena.Data.Query;
+using Hyena.Data.Sqlite;
using Banshee.Collection.Database;
+using Banshee.ServiceStack;
namespace Banshee.SmartPlaylist
{
@@ -46,10 +49,24 @@
public static void MigrateAll ()
{
- Console.WriteLine ("Migrating All.");
+ int version = ServiceManager.DbConnection.QueryInt32 ("SELECT Value FROM CoreConfiguration WHERE Key = 'SmartPlaylistVersion'");
+ if (version == 1)
+ return;
+
Migrator m = new Migrator ();
- foreach (SmartPlaylistSource source in SmartPlaylistSource.LoadAll ())
- m.Migrate (source);
+ using (IDataReader reader = ServiceManager.DbConnection.ExecuteReader (
+ "SELECT SmartPlaylistID, Name, Condition, OrderBy, OrderDir, LimitNumber, LimitCriterion FROM CoreSmartPlaylists")) {
+ while (reader.Read ()) {
+ m.Migrate (
+ Convert.ToInt32 (reader[0]), reader[1] as string,
+ reader[2] as string, reader[3] as string,
+ reader[4] as string, reader[5] as string,
+ reader[6] as string
+ );
+ }
+ }
+
+ ServiceManager.DbConnection.Execute ("INSERT INTO CoreConfiguration (Key, Value) Values ('SmartPlaylistVersion', 1)");
}
public Migrator ()
@@ -69,19 +86,28 @@
order_hash.Add ("LastPlayedStamp ASC", FindOrder ("LastPlayedStamp", "ASC"));
}
- private void Migrate (SmartPlaylistSource source)
+ private void Migrate (int dbid, string Name, string Condition, string OrderBy, string OrderDir, string LimitNumber, string LimitCriterion)
{
- Console.WriteLine ("migrating {0}, order {1}", source.Name, source.OrderBy);
- if (source.OrderBy != null && source.OrderBy != String.Empty) {
- Order order = order_hash [source.OrderBy];
- source.OrderBy = order.Key;
- source.OrderDir = order.Dir;
- }
- source.LimitCriterion = criteria [Convert.ToInt32 (source.LimitCriterion)];
- Console.WriteLine ("Befor: {0}", source.Condition);
- source.Condition = ParseCondition (source.Condition);
- Console.WriteLine ("After: {0}\n", source.Condition);
- //source.Save ();
+ if (OrderBy != null && OrderBy != String.Empty) {
+ Order order = order_hash [OrderBy];
+ OrderBy = order.Key;
+ OrderDir = order.Dir;
+ }
+
+ LimitCriterion = criteria [Convert.ToInt32 (LimitCriterion)];
+ string ConditionXml = ParseCondition (Condition);
+
+ ServiceManager.DbConnection.Execute (new HyenaSqliteCommand (@"
+ UPDATE CoreSmartPlaylists
+ SET Name = ?,
+ Condition = ?,
+ OrderBy = ?,
+ OrderDir = ?,
+ LimitNumber = ?,
+ LimitCriterion = ?
+ WHERE SmartPlaylistID = ?",
+ Name, ConditionXml, OrderBy, OrderDir, LimitNumber, LimitCriterion, dbid
+ ));
}
private string ParseCondition (string value)
@@ -117,7 +143,8 @@
foreach (QueryOperator op in QueryOperator.Operators) {
if (op.MatchesCondition (condition, out col, out v1, out v2)) {
QueryTermNode term = new QueryTermNode ();
- QueryField field = TrackListDatabaseModel.FieldSet.GetByAlias (col);
+ QueryField field = TrackListDatabaseModel.FieldSet [col];
+ bool is_relative_date = false;
if (field == null) {
if (col.IndexOf ("DateAddedStamp") != -1) {
field = TrackListDatabaseModel.FieldSet.GetByAlias ("added");
@@ -125,100 +152,46 @@
field = TrackListDatabaseModel.FieldSet.GetByAlias ("lastplayed");
}
+ // Fix ugly implementation of playlist/smart playlist conditions
+ if (op == QueryOperator.InPlaylist || op == QueryOperator.NotInPlaylist) {
+ field = TrackListDatabaseModel.FieldSet ["playlist"];
+ } else if (op == QueryOperator.InSmartPlaylist || op == QueryOperator.NotInSmartPlaylist) {
+ field = TrackListDatabaseModel.FieldSet ["smartplaylist"];
+ }
+
if (field == null) {
- Console.WriteLine ("got unknown field {0} so skipping", col);
continue;
}
+ is_relative_date = true;
}
- //if (term.Field.IndexOf ("LastPlayedStamp") != -1) {
- //} else if (term.Field.IndexOf ("DateAddedStamp") != -1) {
- //}
+ term.Operator = Operator.GetByUserOperator (op.NewOp);
term.Field = field;
- term.Value = QueryValue.CreateFromUserQuery (v1, field);
- root.AddChild (term);
-
- switch (op.GetType ().ToString ()) {
- case "EQText":
- case "EQ":
- term.Operator = Operator.GetByUserOperator ("==");
- break;
-
- case "NotEQText":
- case "NotEQ":
- term.Operator = Operator.GetByUserOperator ("!=");
- break;
-
- case "Between":
- case "LT":
- term.Operator = Operator.GetByUserOperator ("<");
- break;
-
- case "GT":
- term.Operator = Operator.GetByUserOperator (">");
- break;
-
- case "GTE":
- term.Operator = Operator.GetByUserOperator (">=");
- break;
-
- case "Like":
- term.Operator = Operator.GetByUserOperator (":");
- break;
-
- case "NotLike":
- term.Operator = Operator.GetByUserOperator (":");
- break;
-
- case "StartsWith":
- term.Operator = Operator.GetByUserOperator ("=");
- break;
-
- case "InPlaylist":
- term.Operator = Operator.GetByUserOperator (">");
- break;
-
- case "NotInPlaylist":
- term.Operator = Operator.GetByUserOperator (">");
- break;
-
- case "InSmartPlaylist":
- term.Operator = Operator.GetByUserOperator (">");
- break;
-
- case "NotInSmartPlaylist":
- term.Operator = Operator.GetByUserOperator (">");
- break;
- }
-
- Console.WriteLine ("..{0}\n=>{1}", condition, term.ToString ());
- // Set the column
- /*QueryBuilderMatchRow row = matchesBox.Children[count] as QueryBuilderMatchRow;
- if (!ComboBoxUtil.SetActiveString (row.FieldBox, model.GetName(col))) {
- if (col.IndexOf ("current_timestamp") == -1) {
- Console.WriteLine ("Found col that can't place");
- break;
+ if (op == QueryOperator.Between) {
+ QueryListNode and = new QueryListNode (Keyword.And);
+ QueryTermNode t2 = new QueryTermNode ();
+ t2.Field = term.Field;
+
+ if (is_relative_date) {
+ ParseRelativeDateCondition (term, v1, field, ">=");
+ ParseRelativeDateCondition (t2, v2, field, "<=");
} else {
- bool found = false;
- foreach (string field in model) {
- if (col.IndexOf (model.GetColumn (field)) != -1) {
- ComboBoxUtil.SetActiveString (row.FieldBox, field);
- found = true;
- break;
- }
- }
-
- if (!found) {
- Console.WriteLine ("Found col that can't place");
- break;
- }
+ term.Operator = Operator.GetByUserOperator ("<=");
+ term.Value = QueryValue.CreateFromUserQuery (v1, field);
+ t2.Operator = Operator.GetByUserOperator (">=");
+ t2.Value = QueryValue.CreateFromUserQuery (v2, field);
}
- }*/
-
- // Set the values
- //v1;
- //v2;
+ and.AddChild (term);
+ and.AddChild (t2);
+ root.AddChild (and);
+ } else if (is_relative_date) {
+ ParseRelativeDateCondition (term, v1, field, term.Operator.UserOperator);
+ root.AddChild (term);
+ } else {
+ term.Value = QueryValue.CreateFromUserQuery (v1, field);
+ root.AddChild (term);
+ }
break;
}
@@ -227,7 +200,25 @@
count++;
}
- return root.Trim ().ToXml (TrackListDatabaseModel.FieldSet);
+ QueryNode node = root.Trim ();
+
+ if (node != null) {
+ //Console.WriteLine ("After XML: {0}", node.ToXml (TrackListDatabaseModel.FieldSet, true));
+ //Console.WriteLine ("After SQL: {0}", node.ToSql (TrackListDatabaseModel.FieldSet));
+ }
+
+ return node == null ? String.Empty : node.ToXml (TrackListDatabaseModel.FieldSet);
+ }
+
+ private void ParseRelativeDateCondition (QueryTermNode term, string val, QueryField field, string op)
+ {
+ // Have to flip the operator b/c of how we used to construct the SQL query
+ term.Operator = Operator.GetByUserOperator (op).Dual;
+ DateQueryValue date_value = new DateQueryValue ();
+ // Have to negate the value b/c of how we used to constuct the SQL query
+ date_value.RelativeOffset = -Convert.ToInt64 (val);
+ date_value.Relative = true;
+ term.Value = date_value;
}
private Order FindOrder (string key)
@@ -246,14 +237,16 @@
public sealed class QueryOperator
{
+ public string NewOp;
private string format;
public string Format {
get { return format; }
}
- private QueryOperator (string format)
+ private QueryOperator (string new_op, string format)
{
+ NewOp = new_op;
this.format = format;
}
@@ -294,29 +287,30 @@
}
// calling lower() to have case insensitive comparisons with strings
- public static QueryOperator EQText = new QueryOperator("lower({1}) = {0}{2}{0}");
- public static QueryOperator NotEQText = new QueryOperator("lower({1}) != {0}{2}{0}");
+ public static QueryOperator EQText = new QueryOperator("==", "lower({1}) = {0}{2}{0}");
+ public static QueryOperator NotEQText = new QueryOperator("!=", "lower({1}) != {0}{2}{0}");
- public static QueryOperator EQ = new QueryOperator("{1} = {0}{2}{0}");
- public static QueryOperator NotEQ = new QueryOperator("{1} != {0}{2}{0}");
- public static QueryOperator Between = new QueryOperator("{1} BETWEEN {0}{2}{0} AND {0}{3}{0}");
- public static QueryOperator LT = new QueryOperator("{1} < {0}{2}{0}");
- public static QueryOperator GT = new QueryOperator("{1} > {0}{2}{0}");
- public static QueryOperator GTE = new QueryOperator("{1} >= {0}{2}{0}");
+ public static QueryOperator EQ = new QueryOperator("==", "{1} = {0}{2}{0}");
+ public static QueryOperator NotEQ = new QueryOperator("!=", "{1} != {0}{2}{0}");
+ // TODO how to deal w/ between?
+ public static QueryOperator Between = new QueryOperator("", "{1} BETWEEN {0}{2}{0} AND {0}{3}{0}");
+ public static QueryOperator LT = new QueryOperator("<", "{1} < {0}{2}{0}");
+ public static QueryOperator GT = new QueryOperator(">", "{1} > {0}{2}{0}");
+ public static QueryOperator GTE = new QueryOperator(">=", "{1} >= {0}{2}{0}");
// Note, the following lower() calls are necessary b/c of a sqlite bug which makes the LIKE
// command case sensitive with certain characters.
- public static QueryOperator Like = new QueryOperator("lower({1}) LIKE '%{2}%'");
- public static QueryOperator NotLike = new QueryOperator("lower({1}) NOT LIKE '%{2}%'");
- public static QueryOperator StartsWith = new QueryOperator("lower({1}) LIKE '{2}%'");
- public static QueryOperator EndsWith = new QueryOperator("lower({1}) LIKE '%{2}'");
+ public static QueryOperator Like = new QueryOperator(":", "lower({1}) LIKE '%{2}%'");
+ public static QueryOperator NotLike = new QueryOperator("!:", "lower({1}) NOT LIKE '%{2}%'");
+ public static QueryOperator StartsWith = new QueryOperator("=", "lower({1}) LIKE '{2}%'");
+ public static QueryOperator EndsWith = new QueryOperator(":=", "lower({1}) LIKE '%{2}'");
// TODO these should either be made generic or moved somewhere else since they are Banshee/Track/Playlist specific.
- public static QueryOperator InPlaylist = new QueryOperator("TrackID IN (SELECT TrackID FROM PlaylistEntries WHERE {1} = {0}{2}{0})");
- public static QueryOperator NotInPlaylist = new QueryOperator("TrackID NOT IN (SELECT TrackID FROM PlaylistEntries WHERE {1} = {0}{2}{0})");
+ public static QueryOperator InPlaylist = new QueryOperator("==", "TrackID IN (SELECT TrackID FROM PlaylistEntries WHERE {1} = {0}{2}{0})");
+ public static QueryOperator NotInPlaylist = new QueryOperator("!=", "TrackID NOT IN (SELECT TrackID FROM PlaylistEntries WHERE {1} = {0}{2}{0})");
- public static QueryOperator InSmartPlaylist = new QueryOperator("TrackID IN (SELECT TrackID FROM SmartPlaylistEntries WHERE {1} = {0}{2}{0})");
- public static QueryOperator NotInSmartPlaylist = new QueryOperator("TrackID NOT IN (SELECT TrackID FROM SmartPlaylistEntries WHERE {1} = {0}{2}{0})");
+ public static QueryOperator InSmartPlaylist = new QueryOperator("==", "TrackID IN (SELECT TrackID FROM SmartPlaylistEntries WHERE {1} = {0}{2}{0})");
+ public static QueryOperator NotInSmartPlaylist = new QueryOperator("!=", "TrackID NOT IN (SELECT TrackID FROM SmartPlaylistEntries WHERE {1} = {0}{2}{0})");
public static QueryOperator [] Operators = new QueryOperator [] {
EQText, NotEQText, EQ, NotEQ, Between, LT, GT, GTE, Like, NotLike,
Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistCore.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistCore.cs (original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistCore.cs Fri Jan 25 23:37:29 2008
@@ -46,15 +46,8 @@
{
Gnome.Vfs.Vfs.Initialize();
- Console.WriteLine ("== Migrating Smart Playlists ==");
Migrator.MigrateAll ();
- Console.WriteLine ("== Done Migrating Smart Playlists ==");
- /*new XmlQueryParser (@"
- <foo>
- <bar>
- ");*/
-
// Listen for added/removed sources and added/changed songs
ServiceManager.SourceManager.SourceAdded += HandleSourceAdded;
ServiceManager.SourceManager.SourceRemoved += HandleSourceRemoved;
Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistSource.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistSource.cs (original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistSource.cs Fri Jan 25 23:37:29 2008
@@ -33,6 +33,8 @@
using System.Collections.Generic;
using Mono.Unix;
+
+using Hyena.Data.Query;
using Banshee.Base;
using Banshee.Sources;
@@ -61,13 +63,11 @@
private static string generic_name = Catalog.GetString ("Smart Playlist");
private static string properties_label = Catalog.GetString ("Edit Smart Playlist");
- private string condition;
private string order_by;
private string order_dir;
private string limit_number;
private string limit_criterion;
-
#region Properties
// Source override
@@ -75,6 +75,10 @@
get { return true; }
}
+ public override bool CanRemoveTracks {
+ get { return false; }
+ }
+
// AbstractPlaylistSource overrides
protected override string SourceTable {
get { return "CoreSmartPlaylists"; }
@@ -93,9 +97,30 @@
}
// Custom properties
- public string Condition {
+ private QueryNode condition;
+ public QueryNode ConditionTree {
get { return condition; }
- set { condition = value; }
+ set {
+ condition = value;
+ if (condition != null) {
+ condition_sql = condition.ToSql (TrackListDatabaseModel.FieldSet);
+ condition_xml = condition.ToXml (TrackListDatabaseModel.FieldSet);
+ }
+ }
+ }
+
+ private string condition_sql;
+ public string ConditionSql {
+ get { return condition_sql; }
+ }
+
+ private string condition_xml;
+ public string ConditionXml {
+ get { return condition_xml; }
+ set {
+ condition_xml = value;
+ ConditionTree = XmlQueryParser.Parse (condition_xml, TrackListDatabaseModel.FieldSet);
+ }
}
public string OrderBy {
@@ -150,15 +175,25 @@
{
}
+ /*public SmartPlaylistSource (SmartPlaylistSource original) : this (original.Name)
+ {
+ ConditionXml = original.ConditionXml;
+ OrderBy = original.OrderBy;
+ OrderDir = original.OrderDir;
+ LimitNumber = original.LimitNumber;
+ LimitCriterion = original.LimitCriterion;
+ }*/
+
// For existing smart playlists that we're loading from the database
- public SmartPlaylistSource (int? dbid, string name, string condition, string order_by, string order_dir, string limit_number, string limit_criterion) :
+ public SmartPlaylistSource (int? dbid, string name, string condition_xml, string order_by, string order_dir, string limit_number, string limit_criterion) :
base (generic_name, name, dbid, -1, 0)
{
- Condition = condition;
+ ConditionXml = condition_xml;
OrderBy = order_by;
OrderDir = order_dir;
LimitNumber = limit_number;
LimitCriterion = limit_criterion;
+ DbId = dbid;
Properties.SetString ("SourcePropertiesActionLabel", properties_label);
@@ -194,9 +229,9 @@
{
DbId = ServiceManager.DbConnection.Execute (new BansheeDbCommand (@"
INSERT INTO CoreSmartPlaylists
- (Name, Condition, OrderBy, LimitNumber, LimitCriterion)
- VALUES (?, ?, ?, ?, ?)",
- Name, Condition, OrderBy, LimitNumber, LimitCriterion
+ (Name, Condition, OrderBy, OrderDir, LimitNumber, LimitCriterion)
+ VALUES (?, ?, ?, ?, ?, ?)",
+ Name, ConditionXml, OrderBy, OrderDir, LimitNumber, LimitCriterion
));
}
@@ -207,10 +242,11 @@
SET Name = ?,
Condition = ?,
OrderBy = ?,
+ OrderDir = ?,
LimitNumber = ?,
LimitCriterion = ?
WHERE SmartPlaylistID = ?",
- Name, Condition, OrderBy, LimitNumber, LimitCriterion, DbId
+ Name, ConditionXml, OrderBy, OrderDir, LimitNumber, LimitCriterion, DbId
));
}
@@ -218,24 +254,14 @@
#region DatabaseSource overrides
- public override void Reload ()
+ protected override void RateLimitedReload ()
{
- // TODO don't actually do this here, do it on a timeout
-
// Wipe the member list clean
ServiceManager.DbConnection.Execute (String.Format (
"DELETE FROM CoreSmartPlaylistEntries WHERE SmartPlaylistID = {0}", DbId
));
// Repopulate it
- Console.WriteLine (String.Format (
- @"INSERT INTO CoreSmartPlaylistEntries
- SELECT {0} as SmartPlaylistID, TrackId
- FROM CoreTracks, CoreArtists, CoreAlbums
- WHERE CoreTracks.ArtistID = CoreArtists.ArtistID AND CoreTracks.AlbumID = CoreAlbums.AlbumID
- {1} {2}",
- DbId, PrependCondition("AND"), OrderAndLimit
- ));
ServiceManager.DbConnection.Execute (String.Format (
@"INSERT INTO CoreSmartPlaylistEntries
SELECT {0} as SmartPlaylistID, TrackId
@@ -245,14 +271,14 @@
DbId, PrependCondition("AND"), OrderAndLimit
));
- base.Reload ();
+ base.RateLimitedReload ();
}
#endregion
private string PrependCondition (string with)
{
- return (Condition == null || Condition == String.Empty) ? " " : with + " (" + Condition + ")";
+ return String.IsNullOrEmpty (ConditionSql) ? " " : String.Format ("{0} ({1})", with, ConditionSql);
}
private static Order [] orders = new Order [] {
@@ -286,18 +312,21 @@
using (IDataReader reader = ServiceManager.DbConnection.ExecuteReader (
"SELECT SmartPlaylistID, Name, Condition, OrderBy, OrderDir, LimitNumber, LimitCriterion FROM CoreSmartPlaylists")) {
while (reader.Read ()) {
- SmartPlaylistSource playlist = new SmartPlaylistSource (
- Convert.ToInt32 (reader[0]), reader[1] as string,
- reader[2] as string, reader[3] as string,
- reader[4] as string, reader[5] as string,
- reader[6] as string
- );
- sources.Add (playlist);
+ try {
+ SmartPlaylistSource playlist = new SmartPlaylistSource (
+ Convert.ToInt32 (reader[0]), reader[1] as string,
+ reader[2] as string, reader[3] as string,
+ reader[4] as string, reader[5] as string,
+ reader[6] as string
+ );
+ sources.Add (playlist);
+ } catch (Exception e) {
+ Log.Warning ("Ignoring Smart Playlist", String.Format ("Caught error: {0}", e), false);
+ }
}
}
return sources;
}
-
}
}
Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/DatabaseSource.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/DatabaseSource.cs (original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/DatabaseSource.cs Fri Jan 25 23:37:29 2008
@@ -104,7 +104,7 @@
#region Public Methods
- public virtual void Reload ()
+ public void Reload ()
{
reload_limiter.Execute ();
}
Modified: trunk/banshee/src/Core/Hyena/Hyena.Data.Query/DateQueryValue.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Data.Query/DateQueryValue.cs (original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Data.Query/DateQueryValue.cs Fri Jan 25 23:37:29 2008
@@ -34,6 +34,16 @@
namespace Hyena.Data.Query
{
+ public enum RelativeDateFactor {
+ Second = 1,
+ Minute = 60,
+ Hour = 3600,
+ Day = 3600*24,
+ Week = 3600*24*7,
+ Month = 3600*24*30,
+ Year = 3600*24*365
+ }
+
public class DateQueryValue : QueryValue
{
protected DateTime value;
@@ -48,6 +58,16 @@
get { return value; }
}
+ public bool Relative {
+ get { return relative; }
+ set { relative = value; }
+ }
+
+ public long RelativeOffset {
+ get { return offset; }
+ set { offset = value; IsEmpty = false; }
+ }
+
public override void ParseUserQuery (string input)
{
// TODO: Add support for relative strings like "yesterday", "3 weeks ago", "5 days ago"
@@ -83,6 +103,16 @@
}
}
+ public override void AppendXml (XmlElement node)
+ {
+ if (relative) {
+ node.SetAttribute ("type", "rel");
+ node.InnerText = RelativeOffset.ToString ();
+ } else {
+ base.AppendXml (node);
+ }
+ }
+
public override string ToSql ()
{
return DateTimeUtil.FromDateTime (
Modified: trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryField.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryField.cs (original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryField.cs Fri Jan 25 23:37:29 2008
@@ -101,12 +101,24 @@
return ToTermString (PrimaryAlias, op, value);
}
- public string FormatSql (string format, params object [] args)
+ public string FormatSql (string format, Operator op, string value)
{
- if (Column.IndexOf ("{0}") == -1)
- return String.Format ("{0} {1}", Column, String.Format (format, args));
- else
- return String.Format (Column, String.Format (format, args));
+ if (Column.IndexOf ("{0}") == -1 && Column.IndexOf ("{1}") == -1) {
+ if (ValueType == typeof(StringQueryValue)) {
+ // Match string values literally and against a lower'd version
+ return String.Format ("({0} {1} OR LOWER({0}) {2})", Column,
+ String.Format (format, value),
+ String.Format (format, value.ToLower ())
+ );
+ } else {
+ return String.Format ("{0} {1}", Column, String.Format (format, value));
+ }
+ } else {
+ return String.Format (
+ Column, String.Format (format, value),
+ value, op.IsNot ? "NOT" : null
+ );
+ }
}
public static string ToTermString (string alias, string op, string value)
Modified: trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryNode.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryNode.cs (original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryNode.cs Fri Jan 25 23:37:29 2008
@@ -29,6 +29,7 @@
using System;
using System.Xml;
+using System.IO;
using System.Text;
namespace Hyena.Data.Query
@@ -76,7 +77,12 @@
public abstract void AppendUserQuery (StringBuilder sb);
- public virtual string ToXml (QueryFieldSet fieldSet)
+ public string ToXml (QueryFieldSet fieldSet)
+ {
+ return ToXml (fieldSet, false);
+ }
+
+ public virtual string ToXml (QueryFieldSet fieldSet, bool pretty)
{
XmlDocument doc = new XmlDocument ();
@@ -88,7 +94,19 @@
request.AppendChild (query);
AppendXml (doc, query, fieldSet);
- return doc.OuterXml;
+
+ if (!pretty) {
+ return doc.OuterXml;
+ }
+
+ using (StringWriter sw = new StringWriter ()) {
+ using (XmlTextWriter xtw = new XmlTextWriter (sw)) {
+ xtw.Formatting = System.Xml.Formatting.Indented;
+ xtw.Indentation = 2;
+ doc.WriteTo (xtw);
+ return sw.ToString ();
+ }
+ }
}
public override string ToString ()
Modified: trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryOperator.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryOperator.cs (original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryOperator.cs Fri Jan 25 23:37:29 2008
@@ -38,15 +38,35 @@
{
public string Name;
public string UserOperator;
+
+ public Operator Dual {
+ get {
+ string op = UserOperator.Replace ('>', '^');
+ op = op.Replace ('<', '>');
+ op = op.Replace ('^', '<');
+ return GetByUserOperator (op);
+ }
+ }
+
+ private bool is_not;
+ public bool IsNot {
+ get { return is_not; }
+ }
private static List<Operator> operators = new List<Operator> ();
private static Dictionary<string, Operator> by_op = new Dictionary<string, Operator> ();
private static Dictionary<string, Operator> by_name = new Dictionary<string, Operator> ();
- protected Operator (string name, string userOp)
+
+ protected Operator (string name, string userOp) : this (name, userOp, false)
+ {
+ }
+
+ protected Operator (string name, string userOp, bool is_not)
{
Name = name;
UserOperator = userOp;
+ this.is_not = is_not;
}
static Operator () {
@@ -55,9 +75,10 @@
Add (new Operator ("equals", "=="));
Add (new Operator ("lessThanEquals", "<="));
Add (new Operator ("greaterThanEquals", ">="));
- //Add (new Operator ("notEqual", "!="));
+ Add (new Operator ("notEqual", "!=", true));
+ Add (new Operator ("endsWith", ":="));
Add (new Operator ("startsWith", "="));
- //Add (new Operator ("doesNotContain", "!:"));
+ Add (new Operator ("doesNotContain", "!:", true));
Add (new Operator ("contains", ":"));
Add (new Operator ("lessThan", "<"));
Add (new Operator ("greaterThan", ">"));
Modified: trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryTermNode.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryTermNode.cs (original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Data.Query/QueryTermNode.cs Fri Jan 25 23:37:29 2008
@@ -134,50 +134,66 @@
if (emit_or)
sb.Append (" OR ");
+ string format = null;
switch (Operator.UserOperator) {
case "=": // Starts with
- sb.Append (field.FormatSql ("LIKE '{0}%'", safe_value));
+ format = "LIKE '{0}%'";
+ break;
+
+ case ":=": // Ends with
+ format = "LIKE '%{0}'";
break;
case "==": // Equal to
- sb.Append (field.FormatSql ("= '{0}'", safe_value));
+ format = "= '{0}'";
break;
case "!=": // Not equal to
- sb.Append (field.FormatSql ("!= '{0}'", safe_value));
+ format = "!= '{0}'";
break;
case "!:": // Doesn't contain
- sb.Append (field.FormatSql ("NOT LIKE '%{0}%'", safe_value));
+ format = "NOT LIKE '%{0}%'";
break;
case ":": // Contains
default:
- sb.Append (field.FormatSql ("LIKE '%{0}%'", safe_value));
+ format = "LIKE '%{0}%'";
break;
}
+ sb.Append (field.FormatSql (format, Operator, safe_value));
return true;
}
private bool EmitNumericMatch (StringBuilder sb, QueryField field, bool emit_or)
{
- if (emit_or) {
- sb.Append (" OR ");
- }
-
+ string format;
switch (Operator.UserOperator) {
+ // Operators that don't make sense for numeric types
+ case "!:":
+ case ":=":
+ return false;
+
case ":":
case "=":
case "==":
- sb.Append (field.FormatSql ("= {0}", Value.ToSql ()));
+ format = "= {0}";
break;
default:
- sb.Append (field.FormatSql ("{1} {0}", Value.ToSql (), Operator.UserOperator));
+ format = Operator.UserOperator + " {0}";
break;
}
-
+
+ if (emit_or) {
+ sb.Append (" OR ");
+ }
+
+ sb.Append (field.FormatSql (format,
+ Operator, Value.ToSql ()
+ ));
+
return true;
}
Modified: trunk/banshee/src/Core/Hyena/Hyena.Data.Query/XmlQueryParser.cs
==============================================================================
--- trunk/banshee/src/Core/Hyena/Hyena.Data.Query/XmlQueryParser.cs (original)
+++ trunk/banshee/src/Core/Hyena/Hyena.Data.Query/XmlQueryParser.cs Fri Jan 25 23:37:29 2008
@@ -38,6 +38,11 @@
private string str;
private QueryFieldSet field_set;
+ public static QueryNode Parse (string input, QueryFieldSet fieldSet)
+ {
+ return new XmlQueryParser (input).BuildTree (fieldSet);
+ }
+
public XmlQueryParser () : base () {}
public XmlQueryParser (string str)
@@ -62,7 +67,6 @@
QueryNode node = Parse (query.FirstChild as XmlElement, null);
return (node != null) ? node.Trim () : null;
} catch (Exception e) {
- Console.WriteLine ("Caught exception trying to parse query tree from XML: {0}", e.ToString ());
}
return null;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]