From: <neb...@us...> - 2009-10-07 15:21:57
|
Revision: 1880 http://dl-learner.svn.sourceforge.net/dl-learner/?rev=1880&view=rev Author: nebelschwade Date: 2009-10-07 15:21:39 +0000 (Wed, 07 Oct 2009) Log Message: ----------- - Added last.fm-Support - View/RequestHandler Refactoring - Prepared Recommendation-Fetching and display using ajax - Prepared multi-tag searching using a better SPARQL-Query for tags like "stoner doom drone metal" - Enhanced OWL Modified Paths: -------------- trunk/src/moosique.net/css/style.css trunk/src/moosique.net/index.php trunk/src/moosique.net/js/debug.js trunk/src/moosique.net/js/index.php trunk/src/moosique.net/js/moosique.js trunk/src/moosique.net/js/start.js trunk/src/moosique.net/moosique/classes/Config.php trunk/src/moosique.net/moosique/classes/DllearnerConnection.php trunk/src/moosique.net/moosique/classes/LastFM.php trunk/src/moosique.net/moosique/classes/RequestHandler.php trunk/src/moosique.net/moosique/classes/SparqlQueryBuilder.php trunk/src/moosique.net/moosique/classes/View.php trunk/src/moosique.net/moosique/config.ini trunk/src/moosique.net/moosique/data/allTagsByPopularity.txt trunk/src/moosique.net/moosique/def0.xsd trunk/src/moosique.net/moosique/def1.xsd trunk/src/moosique.net/moosique/index.php trunk/src/moosique.net/moosique/main.wsdl trunk/src/moosique.net/moosique/testing/learnTest.php trunk/src/moosique.net/moosique/testing/main.wsdl trunk/src/moosique.net/moosique/testing/moreThan80.owl Added Paths: ----------- trunk/src/moosique.net/moosique/testing/nodeExtractionBug.conf Modified: trunk/src/moosique.net/css/style.css =================================================================== --- trunk/src/moosique.net/css/style.css 2009-10-05 11:09:22 UTC (rev 1879) +++ trunk/src/moosique.net/css/style.css 2009-10-07 15:21:39 UTC (rev 1880) @@ -1,7 +1,6 @@ -/* moosique.net - a jamendo radio with recommendations! */ @media screen { /*=============== Default Styling ===============*/ -body { font: normal 12px/16px Verdana, Arial, sans-serif; color: #f1f7e4; +body { font: normal 12px/18px Verdana, Arial, sans-serif; color: #f1f7e4; background: url('../img/bg.png') top left repeat #3a3a3a; border-top: 5px solid #1fd611; } input, textarea, select { background: #292929; border: 1px solid #5a5a5a; outline: none; padding: 5px; } input[type=submit] { padding: 5px 10px; } @@ -17,7 +16,7 @@ pre { font: normal 10px/14px Monaco, Courier, monospace; } p, h1, h2, h3, h4, h5, h6, table, ul, ol, blockquote, -pre, form { margin-bottom: 16px; } +pre, form { margin-bottom: 18px; } /* Initial hiding */ #recommendations, #player, @@ -25,11 +24,12 @@ #loadingImg, #header h1 { display: none; } /* Rounded Corners */ -input, textarea, select { -moz-border-radius: 5px; -webkit-border-radius: 5px; } +input, textarea, select { -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; } #content, #status, #playing, -#playerControls, #results li { -moz-border-radius: 10px; -webkit-border-radius: 10px; } +#playerControls, #results li { -moz-border-radius: 10px; -webkit-border-radius: 10px; border-radius: 10px; } #mainMenu a { -moz-border-radius-bottomleft: 10px; -moz-border-radius-bottomright: 10px; - -webkit-border-bottom-left-radius: 10px; -webkit-border-bottom-right-radius: 10px; } + -webkit-border-bottom-left-radius: 10px; -webkit-border-bottom-right-radius: 10px; + border-bottom-left-radius: 10px; border-top-left-radius: 10px; } /* button-click-feeling for links, by offsetting top position */ #playerControls a:active { position: relative; top: 2px; } @@ -75,7 +75,7 @@ #loadingImg { cursor: wait; } -/*=============== Menu ===============*/ +/*=============== Menu & Footer ===============*/ #mainMenu ul { position: absolute; top: 0; right: 0; } #mainMenu li { float: left; } #mainMenu a { display: block; margin-left: 10px; line-height: 30px; height: 30px; @@ -84,23 +84,23 @@ #mainMenu .active a:hover { background: #1fd611; } #mainMenu a:hover { text-decoration: none; background: #5a5a5a; } +#footer a { padding: 0 10px; } /*=============== Search Results ===============*/ #results li h3 a { font-weight: normal; } -#results li img { border: 2px solid #545454; margin-bottom: 16px; } -#results li { display: block; border: 1px solid #545454; padding: 16px; margin-bottom: 16px; - float: left; width: 318px; } -#results li.odd { margin-right: 16px; clear: both; } +#results li img { border: 2px solid #545454; } +#results li { float: left; width: 312px; border: 1px solid #545454; padding: 18px; margin-bottom: 18px; } +#results li.odd { margin-right: 18px; clear: both; } #results ul ul { list-style: disc; } /* This is for the list of found albums etc */ #results li li, #results .artistSearch li li, -#results .tagSearch li li { border: none; display: list-item; padding: 0; margin: 0 0 0 32px; +#results .tagSearch li li { border: none; display: list-item; padding: 0; margin: 0 0 0 36px; width: auto; height: auto; float: none; } #results .tagSearch ul ul { clear: both; } -#results .artistImage { text-align: center; } -#results .cover { margin-right: 16px; width: 110px; float: left; } +#results .artistImage { text-align: center; margin-bottom: 18px;} +#results .cover { margin: 0 18px 18px 0; width: 104px; height: 104px; float: left; } #results .tagSearch h4 { display: inline; } @@ -128,6 +128,4 @@ - - } /* end @media screen */ \ No newline at end of file Modified: trunk/src/moosique.net/index.php =================================================================== --- trunk/src/moosique.net/index.php 2009-10-05 11:09:22 UTC (rev 1879) +++ trunk/src/moosique.net/index.php 2009-10-07 15:21:39 UTC (rev 1880) @@ -22,20 +22,18 @@ </ul> </div> <form id="searchForm" method="get" action="moosique/"> - <ol> - <li> - <select name="searchType" id="searchType"> - <option value="allSearch">All</option> - <option value="artistSearch">Artist</option> - <option value="tagSearch">Tag</option> - <option value="songSearch">Song</option> - <?php /* TODO <option value="lastfm">Last.fm-User</option> */ ?> - </select> - <input id="searchValue" name="searchValue" type="text" /> - <input id="searchSubmit" name="searchSubmit" value="Search" title="Search" type="submit" /> - <img id="loadingImg" src="img/loading.gif" alt="Loading..." /> - </li> - </ol> + <div> + <select name="searchType" id="searchType"> + <option value="allSearch">All</option> + <option value="artistSearch">Artist</option> + <option value="tagSearch">Tag</option> + <option value="songSearch">Song</option> + <option value="lastFM">last.fm</option> + </select> + <input id="searchValue" name="searchValue" type="text" /> + <input id="searchSubmit" name="searchSubmit" value="Search" title="Search" type="submit" /> + <img id="loadingImg" src="img/loading.gif" alt="Loading..." /> + </div> </form> <div id="playerControls"> <a href="#" id="playPause" title="Play/Pause">Play / Pause</a> @@ -44,7 +42,7 @@ <a href="#" id="next" title="Play next Track in Playlist">Next Track</a> <a href="#" id="mute" title="Sound on/off">Mute</a> </div> - <h4 id="status"> </h4> + <div id="status"> </div> <div id="playing"> <span class="info">Player stopped</span> <span class="track">...</span> @@ -63,32 +61,16 @@ your musical taste and generate recommendations. You can find them in the tab »Recommendations«. </p> <p> + You can also enter your <a href="http://last.fm">last.fm</a>-username to automatically use your + most-used tags to generate a initial list of recommendations. + </p> + <p> You can find information about the song currently playing in the tab »Info« and edit and view your current Playlist in the »Playlist«-Tab. </p> <p> Now get started and add something to the Playlist! </p> - - <pre> - <?php - - // recently Heard == posExamples testing - /* - if (!empty($_COOKIE['moosique'])) { - $recent = json_decode(stripslashes($_COOKIE['moosique']))->recentlyListened; - $posExamples = array(); - foreach($recent as $link) { - preg_match_all('#<a\s*(?:rel=[\'"]([^\'"]+)[\'"])?.*?>((?:(?!</a>).)*)</a>#i', $link, $record); - array_push($posExamples, $record[1][0]); - } - } - - print_r(array_unique($posExamples)); - */ - ?> - </pre> - </div> <div id="results"> @@ -97,30 +79,32 @@ <div id="recommendations"> <h2>Recommended Songs</h2> - <p>These are the automatically generated recommendations. Click on a song to add it to your playlist.</p> - <ol id="recommended"> - <li></li> - </ol> + <p> + These recommendations are generated every time you listen to a song + for at least half it's length, assuming that you liked it. + </p> + + <div id="recommendationResults"> + + + </div> + <p> + <a href="#" id="generateRecommendations">Nothing showing up here? You can also + generate your list of recommendations by clicking here.</a> + </p> </div> <div id="information"> <div id="moreInfo"> - <h2>About the Artist</h2> - <img src="http://imgjam.com/albums/8654/covers/1.200.jpg" alt="Cover" /> - <p> - Iusto odio dignissim qui blandit praesent. Nisl ut aliquip ex ea commodo, consequat - duis autem vel eum. Nam liber tempor cum soluta nobis eleifend option congue nihil - imperdiet doming id. In hendrerit eu feugiat nulla luptatum zzril delenit augue duis - dolore te feugait. Quod ii legunt saepius claritas est etiam processus dynamicus - qui nobis videntur parum. - </p> + </div> </div> <div id="player"> <h2>Playlist</h2> <p> - You can delete entries from the playlist by clicking the small x on the left and change their order by clicking on the small up- and down-arrows.<br /> + You can delete entries from the playlist by clicking the small x on the right and + change their order by clicking on the small up- and down-arrows.<br /> </p> <ol id="playlist"> <li></li> @@ -131,6 +115,7 @@ <ol id="recently"> <li></li> </ol> + <p><a href="#" id="reset">Click here to reset your »recently listened to«-list.</a></p> </div> <div id="help"> @@ -140,9 +125,9 @@ </div> <!-- end content --> <div id="footer"> + <a href="http://aksw.org/Projects/DLLearner">DL-Learner</a> | <a href="http://jamendo.com">Jamendo</a> | - <a href="http://mediaplayer.yahoo.com/">Yahoo! Media Player</a> | - <a href="http://aksw.org/Projects/DLLearner">DL-Learner</a> + <a href="http://mediaplayer.yahoo.com/">Yahoo! Media Player</a> </div> </div> <!-- end container --> Modified: trunk/src/moosique.net/js/debug.js =================================================================== --- trunk/src/moosique.net/js/debug.js 2009-10-05 11:09:22 UTC (rev 1879) +++ trunk/src/moosique.net/js/debug.js 2009-10-07 15:21:39 UTC (rev 1880) @@ -1,4 +1,4 @@ -/* activate debugging */ +/* activate debugging if firebug is avaiable */ if (typeof(console) !== 'undefined') { debug = console; debug.log("Firebug-Console for moosique.net activated."); Modified: trunk/src/moosique.net/js/index.php =================================================================== --- trunk/src/moosique.net/js/index.php 2009-10-05 11:09:22 UTC (rev 1879) +++ trunk/src/moosique.net/js/index.php 2009-10-07 15:21:39 UTC (rev 1880) @@ -2,10 +2,10 @@ /* This little Script takes all included js-files and * compresses them with the PHP-Variant of js-min * found here: http://code.google.com/p/jsmin-php/ + * This file is only used in non-debugging-mode */ header('Content-type: application/javascript'); -// set offset to 365 days = 1 year -$offset = 60 * 60 * 24 * 365; +$offset = 60 * 60 * 24 * 365; // set offset to 365 days = 1 year header('Expires: ' . gmdate("D, d M Y H:i:s", time() + $offset) . ' GMT'); ob_start('compress'); @@ -19,7 +19,6 @@ /* the javascript-files to include and compress */ include('mootools-1.2.3-core-yc.js'); include('moosique.js'); -// include('debug.js'); /* no debugging for production */ include('start.js'); ob_end_flush(); Modified: trunk/src/moosique.net/js/moosique.js =================================================================== --- trunk/src/moosique.net/js/moosique.js 2009-10-05 11:09:22 UTC (rev 1879) +++ trunk/src/moosique.net/js/moosique.js 2009-10-07 15:21:39 UTC (rev 1880) @@ -6,13 +6,13 @@ /** * moosique-Player-Class - * - * - * TODO Split out functions and comment it + * + * + * */ var Moosique = new Class({ Implements: Options, - // Some Default Options + // set some default options options: { messageFadeTime: 5000, // Time until a Status message fades out timeToScrobble: 0.5, // factor for calculating max time a user has to listen to a track until its scrobbled @@ -20,8 +20,9 @@ }, /** - * Initializes the Object and sets some default options - * + * Initializes the Object and sets the default options + * activating search functionality and the interface for using the player + * * @param {Object} options */ initialize: function(options) { @@ -31,12 +32,14 @@ this.initInterface(); this.updateRecently(); this.activateSearch(); - /* - this.initPlaylist(); - */ }, + /** + * This function stores references to DOM-Objects in this class + * for easier access in the other methods, if sth. in the DOM is + * changed, just change the references here and everything will work fine. + */ initVars: function() { this.main = document.id('content'); this.menu = document.id('mainMenu'); @@ -59,10 +62,19 @@ this.loading = document.id('loadingImg'); this.welcome = document.id('welcome'); this.help = document.id('help'); - + this.recommendations = document.id('recommendations'); + this.generate = document.id('generateRecommendations'); + this.recResults = document.id('recommendationResults'); + this.reset = document.id('reset'); }, - + /** + * Applies Config-Vars to the Yahoo Media Player as described in http://mediaplayer.yahoo.com/api/ + * for the YMP-Events and initializes the Player. Events are: + * onProgress, onPlaylistUpdate, onTrackPause, onTrackStart, onTrackComplete + * where onProgress is the most important event-handler, because this is where the learning-process is fired + * + */ applyYahooMediaPlayerConfig: function() { var that = this; @@ -73,11 +85,9 @@ /** * progress: Change the track position and duration displayed every time, as usual in players * - * every time the song is played for at least half it's - * playtime, we assume that the song is liked, so we add it - * to the list we create the recommendations from - * (much like last.fm scrobbling) and learn from the heard songs - * BIG TODO + * every time the song is played for a given percent of it's length (set when initializing + * the moosique-Class), we assume that the song is liked (like last.fm-scobling), so we add + * it to the recently listened list AND we create the recommendations and learn */ var progress = function() { that.nowPlayingTime.set('text', @@ -86,13 +96,8 @@ if (Math.ceil(YAHOO.MediaPlayer.getTrackPosition()) == Math.ceil(YAHOO.MediaPlayer.getTrackDuration() * that.options.timeToScrobble)) { - /* This is where the main magic happens - After havin listened to a song for half its time we save this song to the positive examples list - and then send a request to the dllearner who then calulates new recommendations. - - first, we update the cookie with the last played song and then - */ + // first, we update the cookie with the last played song and then var lastListenedListItem = YAHOO.MediaPlayer.getMetaData().anchor.getParent().clone(); var lastListened = lastListenedListItem.getFirst(); @@ -105,6 +110,7 @@ lastListened.getChildren('em').destroy(); // yahoo buttons // this is the final link item we save in the cookie + // stripped of all unneccessary links and buttons, but easy to re-add or learn from var last = lastListenedListItem.get('html'); // get the current cookie @@ -113,7 +119,6 @@ if (recentlyListenedCookie) { // does the cookie exist? recentlyListened = JSON.decode(recentlyListenedCookie).recentlyListened; - if (recentlyListened) { // if the cookie is not totally empty // update recently listened and write the cookie, limit to 10 entries, // due to cookie-max-size of 4KB @@ -134,26 +139,14 @@ // update the recently played list that.updateRecently(); - - // TODO - // send ajax request and save the scrobbled song - // and retrieve and update the recommendations - var getRecommendations = new Request({ - method: 'get', - url: 'moosique/index.php', - onSuccess: function(responseText, responseXML) { - mooPlayer.displayStatusMessage('Added this song to your recently listened to songs.'); - } - }).send(YAHOO.MediaPlayer.getMetaData().title); - - + that.displayStatusMessage('Added this song to your recently listened to songs.'); + that.generateRecommendations(); } }; /** - * playlistUpdate: every time the playlist is updated - * we add the events for delete/up/down-buttons to each - * playlistitem and update the status on what happened + * playlistUpdate: every time the playlist is updated we add the events for + * delete/up/down-buttons to each playlistitem and update the status on what happened */ var playlistUpdate = function() { // delete button @@ -196,10 +189,7 @@ } }); }); - that.displayStatusMessage('Playlist updated.'); - // TODO save to current-playlist cookie? - }; /** @@ -214,6 +204,7 @@ /** * trackStart: we change the Play-Button to a Pause-Button * and Update the status on #now and display whats playing + * TODO: when a track started playing, fetch additional information about artist etc. using musicbrainz */ var trackStart = function() { that.nowPlayingInfo.set('text', 'Currently playing:'); @@ -223,6 +214,7 @@ /** * trackComplete: we change the Pause-Button to a Play-Button + * TODO: if this was the last track, add another track from the recommendations and start playing */ var trackComplete = function() { that.playPause.setStyle('background-position', '0px 0px'); @@ -238,13 +230,37 @@ // Initialize YMP if ready and apply the config YAHOO.MediaPlayer.onAPIReady.subscribe(playerConfig); - }, + /** + * Send an ajax-request to generate the recommendations and passes the + * result to the corresponding html-container + * + */ + generateRecommendations: function() { + var that = this; + + // TODO + // send ajax request and save the scrobbled song + // and retrieve and update the recommendations + var getRecommendations = new Request({ + method: 'get', + url: 'moosique/index.php', + onSuccess: function(response) { + that.recResults.set('html', response); + that.showTab('recommendations'); + that.displayStatusMessage('You have new recommendations!'); + } + }).send('learn=now'); + }, - + /** + * Updates the recently-Listened-To UL-List Element with the contents + * from the recentlyListened-Cookie and makes them re-addable to the playlist + * + */ updateRecently: function() { var that = this; @@ -261,14 +277,17 @@ } that.recently.set('html', recentlyHTML); that.makeAddable(that.recently.getElements('a')); + } else { // cookie is set, but no Songs in recently + that.recently.set('html', '<li></li>'); } + } else { // no Cookie found, emptying recently + that.recently.set('html', '<li></li>'); } }, - - + /** - * adding functionality for the player-GUI and the play, next etc. buttons + * Adds click-events to all player-related buttons, like play, next etc. buttons */ addEventsToButtons: function() { var that = this; @@ -278,8 +297,9 @@ that.next.addEvent('click', function() { YAHOO.MediaPlayer.next(); }); // the Play-Pause Button - that.playPause.addEvent('click', function() { + that.playPause.addEvent('click', function() { // STOPPED: 0, PAUSED: 1, PLAYING: 2,BUFFERING: 5, ENDED: 7 + // see http://mediaplayer.yahoo.com/api/ if (YAHOO.MediaPlayer.getPlayerState() == 0 || YAHOO.MediaPlayer.getPlayerState() == 1 || YAHOO.MediaPlayer.getPlayerState() == 7) { @@ -313,32 +333,11 @@ } }); }, - - /** - * Playlist related functions - */ - initPlaylist: function() { - var that = this; - $$('#recommended a').each(function(a) { - a.addEvent('click', function(e) { - // prevent link following - e.stop(); - a.set('class', 'htrack'); - - var liItem = a.getParent(); - // move to the playlist - liItem.inject(that.playlist); - that.refreshPlaylist(); - }); - }); - - that.refreshPlaylist(); - }, /** - * Refreshes the playlist by emptying the current one - * and reReading the #playlist-container + * Refreshes the YMP-playlist by emptying the current one + * and re-adding all items from the the playlist-container */ refreshPlaylist: function() { var that = this; @@ -350,7 +349,7 @@ /** * Displays a status message * - * @param {Object} message + * @param {String} message */ displayStatusMessage: function(message) { // Update Status and fade out @@ -364,7 +363,9 @@ /** - * initializes interface-functions, clicking buttons and tabs... + * Adds click-Events to the Interface for Tabs and invokes + * addEventsToButtons() + * */ initInterface: function() { var that = this; @@ -376,14 +377,28 @@ }); }); + // generating recommendations clickable + that.generate.addEvent('click', function(e) { + e.stop(); + that.generateRecommendations(); + }); + + that.reset.addEvent('click', function(e) { + e.stop(); + Cookie.dispose('moosique'); + that.updateRecently(); + }); + // make buttons functional this.addEventsToButtons(); - }, /** - * adds events to the search form for retrieving results etc. + * Make the search-Form an ajax-Search form, displaying the results + * on the homepage if successful + * + * TODO: sanitize client-side too using regex and displayStatus */ activateSearch: function() { var that = this; @@ -398,8 +413,15 @@ // show homescreen for resultdisplaying that.showTab('home'); + // if the welcome-text ist present, cut & paste it to help + if (that.welcome) { + if (that.welcome.get('html').length > 100) { + that.help.set('html', that.welcome.get('html')); + that.welcome.destroy(); + } + } that.loading.setStyle('display', 'inline'); - that.results.set('html', '<h2>Searching...</h2><p>Please be patient, this may take a minute or two...</p>'); + that.results.set('html', '<h2>Searching...</h2><p>Please be patient, this may take up to a minute...</p>'); }, onFailure: function() { @@ -410,14 +432,6 @@ that.searchSubmit.erase('disabled'); // reenable submitbutton that.searchSubmit.setStyle('display', 'inline'); that.loading.setStyle('display', 'none'); - - // if the welcome-text ist present, cut & paste it to help - if (that.welcome) { - if (that.welcome.get('html').length > 100) { - that.help.set('html', that.welcome.get('html')); - that.welcome.destroy(); - } - } // display results that.results.set('html', response); // addEvents to result-links @@ -428,28 +442,22 @@ // only send form if value is at least 3 chars long if (that.searchValue.get('value').length > 2) { this.send(); - } + } else { + that.displayStatusMessage('Please enter at least 3 chars for searching...'); + } }); - - - - - - }, - - - - /** * For Recommendations and Search-Results * This function searches for all links with the class addToPlaylist * and makes them addable to the playlist, which means clicking on * them adds them to the playlist and makes them playable. this * is working for links to whole albums and single tracks also + * + * @param {Object} links All links to make addable */ makeAddable: function (links) { var that = this; @@ -485,15 +493,11 @@ }, - - - - - - /** * appends prepared html code to the playlist, empties the playlist if the first * element is an empty li and refreshed the playlist and shows the playlist tab + * + * @param {String} HTML-Code with new Items to add to the playlist */ insertIntoPlaylist: function(newItems) { var that = this; @@ -531,11 +535,9 @@ }, - - - /** - * + * Shows the given tab in the menu, and hides all others + * * @param {String} tabID ID of the Tab to show */ showTab: function(tabID) { @@ -544,17 +546,15 @@ that.menu.getElements('a.' + tabID).getParent().toggleClass('active'); that.main.getChildren().setStyle('display', 'none'); document.id(tabID).setStyle('display', 'block'); - }, - /** * Converts seconds into a string formatted minutes:seconds * with leading zeros for seconds * * @param {Float} seconds - * @return {String} minsec minutes:seconds + * @return {String} minsec minutes:seconds */ secondsToMinutesAndSeconds: function(seconds) { var min = Math.floor(seconds / 60); Modified: trunk/src/moosique.net/js/start.js =================================================================== --- trunk/src/moosique.net/js/start.js 2009-10-05 11:09:22 UTC (rev 1879) +++ trunk/src/moosique.net/js/start.js 2009-10-07 15:21:39 UTC (rev 1880) @@ -2,12 +2,12 @@ var YMPParams = { autoplay: false, parse: false, // do not parse initial content - volume: 1.0, - displaystate: 3 // hide the YMP (1 shows) + volume: 1.0, // play it loud! + displaystate: 3 // hide the YMP-default-GUI (1 shows) }; // Create an instance of the moosique.net -// 0.025 for debugging purposes TODO +// TODO 0.025 for debugging purposes var moo = new Moosique({ timeToScrobble: 0.025 }); // default debug Modified: trunk/src/moosique.net/moosique/classes/Config.php =================================================================== --- trunk/src/moosique.net/moosique/classes/Config.php 2009-10-05 11:09:22 UTC (rev 1879) +++ trunk/src/moosique.net/moosique/classes/Config.php 2009-10-07 15:21:39 UTC (rev 1880) @@ -26,8 +26,10 @@ } } + /** - * + * Returns the value of a general config-entry from config.ini + * * @return String The wanted Configvalue * @param String Value for the wanted Configvalue */ @@ -35,8 +37,21 @@ return $this->config['general'][$value]; } + /** - * + * Returns the value of a last-fm config-entry from config.ini + * + * @return String The wanted Configvalue + * @param String Value for the wanted Configvalue + */ + public function getConfigLastFM($value) { + return $this->config['lastFM'][$value]; + } + + + /** + * Returns the value of an URL defined in config.ini + * * @return String The wanted Url * @param String Value for the wanted Url */ @@ -44,17 +59,45 @@ return $this->config['url'][$value]; } - public function getLearningConfig() { - return $this->config['learning']; + + /** + * This funtion returns one (if specified) or all learning-Config entries from config.ini + * + * @param String Value for a single learning-Configuration + * @return Mixed The wanted value as a string, or - if not specified - complete learingConfig as an array + */ + + public function getConfigLearning($prefix = false) { + if ($prefix !== false) { + if (isset($this->config['learning'][$prefix])) { + return $this->config['learning'][$prefix]; + } else { + return false; + } + } else { + return $this->config['learning']; + } } + /** - * - * @return Array An associative array of all prefixes + * This funtion returns one (if specified) or all prefixes from the config.ini + * + * @param String String-Value for a single prefix + * @return Mixed The wanted prefix as a string, or - if not specified - all Prefixes as an array */ - public function getAllPrefixes() { - return $this->config['prefix']; - } + public function getConfigPrefixes($prefix = false) { + if ($prefix !== false) { + if (isset($this->config['prefix'][$prefix])) { + return $this->config['prefix'][$prefix]; + } else { + return false; + } + } else { + return $this->config['prefix']; + } + } + } Modified: trunk/src/moosique.net/moosique/classes/DllearnerConnection.php =================================================================== --- trunk/src/moosique.net/moosique/classes/DllearnerConnection.php 2009-10-05 11:09:22 UTC (rev 1879) +++ trunk/src/moosique.net/moosique/classes/DllearnerConnection.php 2009-10-07 15:21:39 UTC (rev 1880) @@ -22,9 +22,9 @@ $this->setEndpoint($this->getConfigUrl('jamendo')); // load WSDL files (has to be done due to a Java web service bug) + ini_set('soap.wsdl_cache_enabled', '0'); include('Utilities.php'); - ini_set('soap.wsdl_cache_enabled', '0'); - // Utilities::loadWSDLfiles($this->getConfigUrl('wsdl')); + Utilities::loadWSDLfiles($this->getConfigUrl('wsdl')); $this->connect(); } @@ -111,23 +111,29 @@ * * */ - public function learn($instances, $positiveExamples, $owlfile) { - $id = $_SESSION['sessionID']; - $conf = $this->getLearningConfig(); + public function learn($instances, $positiveExamples) { + $result = false; + // $id = $_SESSION['sessionID']; - // TODO? use this as knowledgesource ID? - $this->client->addKnowledgeSource($id, 'owlfile', $owlfile); + // TODO for learning we create a new learning-ID and knowledge-Source + $id = $this->client->generateID(); + $kID = $this->client->addKnowledgeSource($id, 'sparql', $this->endpoint); + $conf = $this->getConfigLearning(); + + $this->client->addKnowledgeSource($id, 'owlfile', $this->getConfigUrl('tagOntology')); $this->client->setReasoner($id, $conf['reasoner']); - // set the instances and pos examples - $this->client->applyConfigEntryStringArray($id, $this->knowledgeSourceID, 'instances', $instances); + // set the instances, the learning-Problem and pos examples + // $this->client->applyConfigEntryStringArray($id, $this->knowledgeSourceID, 'instances', $instances); + $this->client->applyConfigEntryStringArray($id, $kID, 'instances', $instances); $this->client->setLearningProblem($id, $conf['problem']); - $this->client->setPositiveExamples($id, $positiveExamples); // recursion-depth and fragment saving - $this->client->applyConfigEntryInt($id, $this->knowledgeSourceID, 'recursionDepth', $conf['recursionDepth']); - $this->client->applyConfigEntryBoolean($id, $this->knowledgeSourceID, 'saveExtractedFragment', $conf['saveExtractedFragment']); + // $this->client->applyConfigEntryInt($id, $this->knowledgeSourceID, 'recursionDepth', $conf['recursionDepth']); + // $this->client->applyConfigEntryBoolean($id, $this->knowledgeSourceID, 'saveExtractedFragment', $conf['saveExtractedFragment']); + $this->client->applyConfigEntryInt($id, $kID, 'recursionDepth', $conf['recursionDepth']); + $this->client->applyConfigEntryBoolean($id, $kID, 'saveExtractedFragment', $conf['saveExtractedFragment']); // algorithm config $learnID = $this->client->setLearningAlgorithm($id, $conf['algorithm']); @@ -135,20 +141,26 @@ $this->client->applyConfigEntryInt($id, $learnID, 'valueFrequencyThreshold', $conf['valueFrequencyThreshold']); $this->client->applyConfigEntryBoolean($id, $learnID, 'useHasValueConstructor', $conf['useHasValueConstructor']); - $this->client->applyConfigEntryStringTupleList($id, $this->knowledgeSourceID, 'replacePredicate', array( - "http://www.holygoat.co.uk/owl/redwood/0.1/tags/taggedWithTag"), array("http://www.w3.org/1999/02/22-rdf-syntax-ns#type") + // replace prefixes + // $this->client->applyConfigEntryStringTupleList($id, $this->knowledgeSourceID, 'replacePredicate', + $this->client->applyConfigEntryStringTupleList($id, $kID, 'replacePredicate', + array($this->getConfigPrefixes('tags') . 'taggedWithTag'), array($this->getConfigPrefixes('rdf') . 'type') ); $this->client->initAll($id); + $result = $this->client->learnDescriptionsEvaluated($id); + $result = json_decode($result); + return $result; - $concepts = false; - - $concepts = $this->client->learnDescriptionsEvaluated($id); - $concepts = json_decode($concepts); + } + + + public function kbToSqarql($kb) { + return $this->client->SparqlRetrieval($kb, 20); - return $concepts; + } } Modified: trunk/src/moosique.net/moosique/classes/LastFM.php =================================================================== --- trunk/src/moosique.net/moosique/classes/LastFM.php 2009-10-05 11:09:22 UTC (rev 1879) +++ trunk/src/moosique.net/moosique/classes/LastFM.php 2009-10-07 15:21:39 UTC (rev 1880) @@ -1,83 +1,106 @@ <?php -/** - * TODO: UGLY! Use last.fm-Api instead? - * API-Key: b9877a7391416d0846ad5f240a1838f9 - * - * Later. - */ -class LastFM { +class LastFM extends Config { + + private $topTags; - private $data; - private $config; - private $username; - - function __construct($config, $username) { - $this->config = $config; - $this->username = $username; - $this->getData(); + function __construct($user) { + parent::__construct(); // init config + + $this->getLastFMTags($user); } - function getData() { - include_once('arc/ARC2.php'); - $rdfParser = ARC2::getRDFParser(); - $lastfmResource = $this->config->getUrl('lastfm') . urlencode($this->username); - $rdfParser->parse($lastfmResource); - // parse, non simple array - $index = $rdfParser->getSimpleIndex(0); - $this->data = $index; + /** + * + * + * + */ + private function getLastFMTags($user) { + $allTags = array(); + $requestUrl = $this->getConfigLastFM('topTagsUrl') + . '&user=' . $user + . '&api_key=' . $this->getConfigLastFM('apiKey'); + $lastFMTags = @simplexml_load_file($requestUrl); + + if ($lastFMTags) { // meaning the last.fm-username exists + foreach($lastFMTags->toptags as $tags) { + foreach($tags as $tag) { + $allTags[] = (String)$tag->name; + } + } + // we limit the array to the 10 most used tags + $allTags = array_slice($allTags, 0, 10); + + // if there is a last-fm user, but he has tagged nothing? + // we get the list of top-artists and get the topTags from the artists + if (empty($allTags)) { + $allTags = $this->getTagsFromTopArtists($user); + } + } else { + if ($this->debugger) $this->debugger->log($user, 'The last.fm-User does not exist. Please try again.'); + } + $this->topTags = $allTags; } - function getRecentTracks() { - $playedTracks = array(); - $trackNodes = array(); + /** + * + * + * + */ + private function getTagsFromTopArtists($user) { + $allArtists = array(); + $finalTags = array(); - echo '<pre>'; - // print_r($this->data); - - if (is_array($this->data) && !empty($this->data)) { - foreach($this->data as $rootItem => $rootValue) { - // only process further if the rootitem ist no uri - if (!preg_match('/http:\/\//i', $rootItem)) { - foreach($rootValue as $childItem => $childValue) { - // if there is a childitem :track_played, we can use the information - if ($childItem == $this->config->getPrefix('played')) { - $trackNodes[] = $childValue[0]['value']; - } - } - } + // get the top artists for the user + $requestUrl = $this->getConfigLastFM('topArtistsUrl') + . '&user=' . $user + . '&api_key=' . $this->getConfigLastFM('apiKey'); + + $lastFMArtists = @simplexml_load_file($requestUrl); + + foreach($lastFMArtists->topartists as $artists) { + foreach($artists as $artist) { + $allArtists[] = (String)$artist->name; } - } else { - echo 'Data-Array empty.'; } + // reduce top Artists to TOP 10 + $allArtists = array_slice($allArtists, 0, 10); + + // get the topTags for every artist - if (!empty($trackNodes)) { - foreach($trackNodes as $trackNode) { - $track = $this->data[$trackNode][$this->config->getPrefix('title')][0]['value']; - $artistNode = $this->data[$trackNode][$this->config->getPrefix('maker')][0]['value']; - $artist = $this->data[$artistNode][$this->config->getPrefix('name')][0]['value']; - $artistZitgist = $this->data[$artistNode][$this->config->getPrefix('same')][0]['value']; - $album = ''; - $albumZitgist = ''; - - $playedTracks[] = array($artist, $track, $album, $artistZitgist, $albumZitgist); - + foreach($allArtists as $artistName) { + $requestUrl = $this->getConfigLastFM('artistsTopTagsUrl') + . '&artist=' . urlencode($artistName) + . '&api_key=' . $this->getConfigLastFM('apiKey'); + $artistTags = @simplexml_load_file($requestUrl); + + // take only the first two tags, that should be enough + foreach($artistTags->toptags as $tags) { + $someCounter = 0; + foreach($tags as $tag) { + $finalTags[] = (String)$tag->name; + $someCounter++; + if ($someCounter == 2) break; + } } - } else { - echo "No recently played tracks avaiable from last.fm."; } + // remove double entries and limit the array to the TOP 10 + $finalTags = array_unique($finalTags); + $finalTags = array_slice($finalTags, 0, 10); - print_r($trackNodes); - print_r($playedTracks); - echo '</pre>'; + return $finalTags; } - - + + /** + * + * + * + */ + public function getTopTags() { + return $this->topTags; + } + + } -include('config.php'); - -$lastfm = new LastFM($conf, 'nebelschwade'); -$lastfm->getRecentTracks(); - ?> \ No newline at end of file Modified: trunk/src/moosique.net/moosique/classes/RequestHandler.php =================================================================== --- trunk/src/moosique.net/moosique/classes/RequestHandler.php 2009-10-05 11:09:22 UTC (rev 1879) +++ trunk/src/moosique.net/moosique/classes/RequestHandler.php 2009-10-07 15:21:39 UTC (rev 1880) @@ -20,103 +20,199 @@ if ($this->isAjax() && $this->isGet()) { $this->response = $this->processRequest(); } else { - // TODO + // we do nothing. This application is based on AJAX. } } /** - * Returns the HTML stored in the private $response - * - * @return String HTML-Code generated - */ - public function getResponse() { - return $this->response; - } - - /** * Processes the request made through ajax, handles * the different searches and other related requests * * @return */ private function processRequest() { + $response = ''; - // general search + // general search for all / artist / tags / song + // ====================================================================== if (isset($_GET['searchType']) && isset($_GET['searchValue'])) { - - $search = $this->cleanString($_GET['searchValue']); + // clean up the search value + $search = $this->cleanString($_GET['searchValue'], $_GET['searchType']); + // a search for "everything" causes 3 searches for artist, tags and songs + // concatenating the response from all 3 searches if ($_GET['searchType'] == 'allSearch') { + // artist + $data = $this->getData($search, 'artistSearch'); + $view = new View($data, 'artistSearch'); + $response .= $view->getHTML(); + // tag + $data = $this->getData($search, 'tagSearch'); + $view = new View($data, 'tagSearch'); + $response .= $view->getHTML(); + // song + $data = $this->getData($search, 'songSearch'); + $view = new View($data, 'songSearch'); + $response .= $view->getHTML(); - // TODO doing 3times the same thing is ugly, build function doing this - - // Artists - $sparql = new SparqlQueryBuilder($search, 'artistSearch', 20); - $query = $sparql->getQuery(); - $json = $this->connection->sparqlQuery($query); - $result = json_decode($json); - $artistObject = $result->results->bindings; - $artistView = new View('artistSearch', $artistObject); - $artistResponse = $artistView->getHTML(); - - // Tags - $sparql = new SparqlQueryBuilder($search, 'tagSearch', 20); - $query = $sparql->getQuery(); - $json = $this->connection->sparqlQuery($query); - $result = json_decode($json); - $tagObject = $result->results->bindings; - $tagView = new View('tagSearch', $tagObject); - $tagResponse = $tagView->getHTML(); - - - // Songs - $sparql = new SparqlQueryBuilder($search, 'songSearch', 20); - $query = $sparql->getQuery(); - $json = $this->connection->sparqlQuery($query); - $result = json_decode($json); - $songObject = $result->results->bindings; - $songView = new View('songSearch', $songObject); - $songResponse = $songView->getHTML(); - - // merge results, and return it - return $artistResponse . $tagResponse . $songResponse; - - } else { // normal tag/artist/song-search - - $sparql = new SparqlQueryBuilder($search, $_GET['searchType']); - $query = $sparql->getQuery(); - + } - // sparql-query to dellearner - $json = $this->connection->sparqlQuery($query); - // convert to useable object - $result = json_decode($json); - $resultObject = $result->results->bindings; - - // create and return the response - $view = new View($_GET['searchType'], $resultObject); + if ($_GET['searchType'] == 'artistSearch' || + $_GET['searchType'] == 'tagSearch' || + $_GET['searchType'] == 'songSearch') { + // normal search for artist, tag or song + $data = $this->getData($search, $_GET['searchType']); + $view = new View($data, $_GET['searchType']); $response = $view->getHTML(); + } - return $response; + + if ($_GET['searchType'] == 'lastFM') { + $lastFM = new LastFM($search); + $tags = $lastFM->getTopTags(); + // no we have the topTags, do a search for related albums + foreach($tags as $tag) { + // TODO when using last.fm-Tags for tagSearch, we want exakt results, meaning + // no "darkmetalbeermusic" when the lastfm tag is "metal" + + $tag = $this->cleanString($tag, $_GET['searchType']); + // displaying 10 results per tag (=100 results) should be enough + $data = $this->getData($tag, $_GET['searchType'], 20); + $view = new View($data, $_GET['searchType']); + $response .= $view->getHTML(); + } + } } - // TODO other requuests + // TODO other requests --- artist Information - // a playlist is requested. due to a bug in ymp we don't - // directly deliver the .xspf-file, we return a prepared - // list of <li> including the links to the mp3-files + + + + // A Learning-Request + // ====================================================================== + if (isset($_GET['learn']) && $_GET['learn'] == 'now') { + $posExamples = $this->getPositiveExamples(); + $instances = $this->getInstances($posExamples); + + $this->debugger->log($instances, "INSTANZEN"); + $this->debugger->log($posExamples, "posExamples"); + + $res = $this->connection->learn($instances, $posExamples); + + // TODO (Yes, BIG one) + // build sparql-query based on learning-results + // build html-view build an sparql-results --- misuse tagSearch-Thingie + foreach ($res as $solution) { + $response .= round($solution->scoreValue*100, 2) . '% --- ' . $solution->descriptionKBSyntax . "\n"; + // $response .= $this->connection->kbToSqarql($solution->descriptionKBSyntax) . "\n"; + } + + $response = '<pre>' . $response . '</pre>'; + + } + + + // A Playlist-Request + // ====================================================================== if (isset($_GET['get']) && isset($_GET['playlist'])) { - $view = new View($_GET['get'], - array('playlist' => $_GET['playlist'], 'albumID' => $_GET['rel']) + // Due to a bug in YMP we don't directly deliver the .xspf-file, we return a prepared + // list of <li> including the links to the mp3-files, and this is build in the view of course. + $data = $this->prepareData( + array('playlist' => $_GET['playlist'], 'albumID' => $_GET['rel']), + $_GET['get'] ); - return $view->getHTML(); + $view = new View($data, $_GET['get']); + $response = $view->getHTML(); } + + + + // Finally returning the response + if (!empty($response)) { + return $response; + } else { + if ($_GET['searchType'] == 'lastFM') { + return '<h2>Nothing found for the last.fm-user »' . $_GET['searchValue'] . '«.'; + } + return "<h2>The repsonse from the server was empty.</h2>"; + } } + /** + * + * + * + */ + private function getPositiveExamples() { + $posExamples = array(); + if (!empty($_COOKIE['moosique'])) { + $recent = json_decode(stripslashes($_COOKIE['moosique']))->recentlyListened; + foreach($recent as $link) { + // extract relation from the cookie-link + preg_match_all('#<a\s*(?:rel=[\'"]([^\'"]+)[\'"])?.*?>((?:(?!</a>).)*)</a>#i', $link, $record); + array_push($posExamples, $record[1][0]); + } + } + + $posExamples = array_unique($posExamples); + return $posExamples; + + } + + /** + * + * + * + */ + private function getInstances($posExamples) { + // TODO more testing, what is the optimum posExamples/neutral ratio, 50/50? + // for now we assume 50/50 + // $totalInstances = $this->getConfigLearning('instances'); + $instances = array(); + // and then add some random Records _not_ in this list + $allRecords = file($this->getConfigUrl('allRecords')); + $countPos = count($posExamples); + + for ($i = 0; $i < $countPos; $i++) { + $randomRecord = trim($allRecords[array_rand($allRecords)]); + // no double entries for the $instances-array + if (!in_array($randomRecord, $posExamples)) { + array_push($instances, $randomRecord); + } + } + // merge with posExamples + $instances = array_merge($posExamples, $instances); + shuffle($instances); + + return $instances; + } + + + /** + * + * @param Integer $limit optional Limit for Sparql-Query + */ + private function getData($search, $type, $limit = 0) { + $sparql = new SparqlQueryBuilder($search, $type, $limit); + $query = $sparql->getQuery(); + // sparql-query to dellearner + $json = $this->connection->sparqlQuery($query); + // convert to useable object + $result = json_decode($json); + $resultObject = $result->results->bindings; + + // prepare the data for HTML processing + $data = $this->prepareData($resultObject, $type); + return $data; + } + + + /** * Establishes a new Dl-Learner Connection and saves it in - * private connection for class-wide use. + * private $connection for class-wide use. * * @return */ @@ -125,16 +221,172 @@ } /** - * Removes unwanted chars from a string - * + * Removes unwanted chars from a search-string + * + * TODO - NOT IMPLEMENTED but prepared + * If the search string contains a %20-Space-Char somewhere in the middle + * of the string, it returns an array of search-values, divided by " " + * Doing this we later can perform more searches for values like "stoner doom metal" + * resulting in an array with [0] => stoner [1] => doom [2] => metal */ - private function cleanString($string) { - $remove = array(' ', '/', '?', '-', '_', '+', "=", '$', ',', '.', ':', ';', '\'', "\"", "\\", "\\\\"); + private function cleanString($string, $type) { + // $remove = array('/', '?', '+', "=", '$', ',', '.', ':', ';', '\'', "\"", "\\", "\\\\"); + $remove = array(' ', '/', '?', '+', "=", '$', ',', '.', ':', ';', '\'', "\"", "\\", "\\\\"); + $string = trim($string); $string = str_replace($remove, '', $string); + // and remove double whitespaces + $string = str_replace(array(' ', ' '), ' ', $string); + + // when searching for tags due to the jamendo-tag handling we split + // tags into an array for better filter-results + /* + if ((strpos($string, " ") > 0) && $type = 'tagSearch') { + $string = explode(" ", $string); + $this->debugger->log($string, "MEHR ALS ZWEI"); + } + */ return $string; } + + /** + * Cleans up objects or retrieves them from a XML-File (for playlists) + * and converts them into arrays for use in the view class + * + * @param Mixed $data The Data-Object (from a Sparql-Query or a playlist-array) + * @param String $type To define what kind of data to prepare + * @return Array A multidimensional Array ready for processing for HTML-output + */ + private function prepareData($data, $type) { + $mergedArray = array(); + + switch ($type) { + case 'artistSearch' : + $mergedArray = $this->mergeArray($data, 'artist'); + $mergedArray = $this->arrayUnique($mergedArray); + break; + + case 'tagSearch' : + $mergedArray = $this->mergeArray($data, 'tag'); + break; + + case 'songSearch' : + $mergedArray = $this->mergeArray($data, 'track'); + $mergedArray = $this->arrayUnique($mergedArray); + break; + + case 'lastFM' : + $mergedArray = $this->mergeArray($data, 'tag'); + break; + + case 'albumPlaylist' : + $playlistObject = simplexml_load_file($data['playlist']); + $mergedArray = $this->object2array($playlistObject); + // prepend the album stream-information + $mergedArray['albumID'] = $data['albumID']; + break; + + case 'trackPlaylist' : + $playlistObject = simplexml_load_file($data['playlist']); + $mergedArray = $this->object2array($playlistObject); + $mergedArray['albumID'] = $data['albumID']; + break; + } + + return $mergedArray; + } + + + /** + * TOOD implement nice merging for multi-tag-search + * This function merges the result-Object to a nice array + * we can process easily. The array is created by type, + * returning the data sorted for artist, tag or song + * + * @param Object $data + * @param String $type This can be 'artist', 'tag' or 'song' + * @return Array A Multidimensional array sorted by type for output-use + */ + private function mergeArray($data, $type) { + // convert the $data-response object to an array + $array = $this->object2array($data); + $combinedArray = array(); + + foreach($array as $subArray) { + if (!array_key_exists($subArray[$type]['value'], $combinedArray)) { + $combinedArray[$subArray[$type]['value']] = $subArray; + } else { + // we already have an object with this tag? -> merge! + $combinedArray[$subArray[$type]['value']] = array_merge_recursive( + $combinedArray[$subArray[$type]['value']], $subArray + ); + } + } + + if (!empty($combinedArray)) { + return $combinedArray; + } else return false; + } + + + /** + * Like the php-function array_unique, but for multidimensional arrays, calls itself recursively + * + * + * @return Array (Multidimensional) array without double entries + * @param Array $array The Array to clean up + */ + private function arrayUnique($array) { + $newArray = array(); + if (is_array($array)) { + foreach($array as $key => $val) { + if ($key != 'type' && $key != 'datatype') { + if (is_array($val)) { + $val2 = $this->arrayUnique($val); + } else { + $val2 = $val; + $newArray = array_unique($array); + break; + } + if (!empty($val2)) { + $newArray[$key] = $val2; + } + } + } + } + return $newArray; + } + + + /** + * Converts a simple Object to an array + * + * @return Array the Array created from the Object + * @param object $obj The Object to convert + */ + private function object2array($obj) { + $arr = array(); + $_arr = is_object($obj) ? get_object_vars($obj) : $obj; + foreach ($_arr as $key => $val) { + $val = (is_array($val) || is_object($val)) ? $this->object2array($val) : $val; + $arr[$key] = $val; + } + return $arr; + } + + + /** + * Returns the HTML stored in the private $response + * + * @return String HTML-Code generated + */ + public function getResponse() { + return $this->response; + } + + + /** * Checks if the request made is an AJAX-Request and returns true if so * * @return Boolean @@ -148,6 +400,7 @@ } } + /** * Checks if the request made is a GET-Request and returns true if so * @@ -161,6 +414,7 @@ } } + /** * Checks if the request made is a POST-Request and returns true if so * @@ -173,9 +427,7 @@ return false; } } + } - - - ?> \ No newline at end of file Modified: trunk/src/moosique.net/moosique/classes/SparqlQueryBuilder.php =================================================================== --- trunk/src/moosique.net/moosique/classes/SparqlQueryBuilder.php 2009-10-05 11:09:22 UTC (rev 1879) +++ trunk/src/moosique.net/moosique/classes/SparqlQueryBuilder.php 2009-10-07 15:21:39 UTC (rev 1880) @@ -28,7 +28,6 @@ * Builds the complete Sparql-Query depending on the type of search * and saves it in the private $queryString. * - * @return * @param object $search * @param object $typeOfSearch * @param object $limit @@ -37,7 +36,7 @@ /* Build up the Prefixes */ $prefixes = ''; - foreach($this->getAllPrefixes() as $prefix => $resource) { + foreach($this->getConfigPrefixes() as $prefix => $resource) { $prefixes .= 'PREFIX ' . $prefix . ': <' . $resource . '>' . "\n"; } @@ -62,6 +61,8 @@ case 'artistSearch' : $query = $this->queryArtistSearch($search); break; case 'tagSearch' : $query = $this->queryTagSearch($search); break; case 'songSearch' : $query = $this->querySongSearch($search); break; + case 'lastFM' : $query = $this->queryTagSearch($search); break; + /* TODO build functions for other queries case 'albumInfo' : $query = $this->queryAlbumInfo($search); break; case 'songInfo' : $query = $this->querySongInfo($search); break; @@ -76,6 +77,7 @@ /** * Returns the Sparql-Query part for an artist-search * + * @param Mixed * @return String Sparql-Query part for artist-search */ private function queryArtistSearch($search) { @@ -92,10 +94,9 @@ }'; // we want the xspf-playlist only, the search filters is // flagged with "i" for case-insensitive search - $queryString .= ' - FILTER (regex(str(?playlist), "xspf", "i")) . - FILTER (regex(str(?artistName), "' . $search . '", "i")) . - '; + $queryString .= 'FILTER (regex(str(?playlist), "xspf", "i")) . '; + $queryString .= 'FILTER (regex(str(?artistName), "' . $search . '", "i")) . '; + return $queryString; } @@ -109,25 +110,61 @@ * @return String Sparql-Query part for tag-search */ private function queryTagSearch($search) { + /* TODO multi-tag search -- maybe building an extra function is better for this + $moreThanOneTag = is_array($search); + $searchCount = 0; + + if ($moreThanOneTag === true) { + $searchCount = count($search); + } + */ $queryString = ' { ?artist rdf:type mo:MusicArtist ; foaf:name ?artistName ; foaf:made ?record . ?record rdf:type mo:Record ; - dc:title ?albumTitle ; - tags:taggedWithTag ?tag ; - mo:available_as ?playlist . - + dc:title ?albumTitle ; '; + /* + if ($moreThanOneTag === true) { + for ($i = 0; $i < $searchCount; $i++) { + $queryString .= ' tags:taggedWithTag ?tag' . $i . ' ; '; + } + } else { + */ + $queryString .= ' tags:taggedWithTag ?tag ; '; + /* + } + */ + $queryString .= ' mo:available_as ?playlist . OPTIONAL { ?record mo:image ?cover . FILTER (regex(str(?cover), "1.100.jpg", "i")) . } - }'; - $queryString .= ' - FILTER (regex(str(?playlist), "xspf", "i")) . - FILTER (regex(str(?tag), "' . $search . '", "i")) . - '; - return $queryString; + } '; + // we want the xspf-playlist only, the search filters is + // flagged with "i" for case-insensitive search + $queryString .= ' FILTER (regex(str(?playlist), "xspf", "i")) . '; + + // searching for more than on value? + /* + if (is_array($search)) { + $queryString .= ' FILTER ('; + + // glueing th... [truncated message content] |