got qualified browse working, just needs style

This commit is contained in:
tmont 2011-02-16 10:11:55 +00:00
parent 4b877df2d9
commit 973b8196d9
16 changed files with 125 additions and 56 deletions

View File

@ -13,12 +13,12 @@
<many-to-one name="Creator" column="creator" not-null="true" foreign-key="fk_game_user"/> <many-to-one name="Creator" column="creator" not-null="true" foreign-key="fk_game_user"/>
<set access="field" name="systems" table="game_system_map" cascade="save-update"> <set access="nosetter.camelcase" name="Systems" table="game_system_map" cascade="save-update">
<key column="game_id" /> <key column="game_id" />
<many-to-many class="VideoGameQuotes.Api.GamingSystem" column="system_id" /> <many-to-many class="VideoGameQuotes.Api.GamingSystem" column="system_id" />
</set> </set>
<set access="field" name="publishers" table="game_publisher_map" cascade="save-update"> <set access="nosetter.camelcase" name="Publishers" table="game_publisher_map" cascade="save-update">
<key column="game_id" /> <key column="game_id" />
<many-to-many class="VideoGameQuotes.Api.Publisher" column="publisher_id" /> <many-to-many class="VideoGameQuotes.Api.Publisher" column="publisher_id" />
</set> </set>

View File

@ -13,17 +13,17 @@
<many-to-one name="Creator" column="creator" not-null="true" foreign-key="fk_quote_user"/> <many-to-one name="Creator" column="creator" not-null="true" foreign-key="fk_quote_user"/>
<many-to-one name="Game" column="game_id" not-null="true" foreign-key="fk_quote_game" cascade="save-update" /> <many-to-one name="Game" column="game_id" not-null="true" foreign-key="fk_quote_game" cascade="save-update" />
<set access="field" name="categories" table="quote_category_map" cascade="save-update"> <set access="nosetter.camelcase" name="Categories" table="quote_category_map" cascade="save-update">
<key column="quote_id" /> <key column="quote_id" />
<many-to-many class="VideoGameQuotes.Api.Category" column="category_id" /> <many-to-many class="VideoGameQuotes.Api.Category" column="category_id" />
</set> </set>
<set access="field" name="votes" table="vote" cascade="save-update"> <set access="nosetter.camelcase" name="Votes" table="vote" cascade="save-update">
<key column="quote_id" /> <key column="quote_id" />
<one-to-many class="VideoGameQuotes.Api.Vote" /> <one-to-many class="VideoGameQuotes.Api.Vote" />
</set> </set>
<set access="field" name="flags" table="quote_flag" cascade="save-update"> <set access="nosetter.camelcase" name="Flags" table="quote_flag" cascade="save-update">
<key column="quote_id" /> <key column="quote_id" />
<one-to-many class="VideoGameQuotes.Api.QuoteFlag" /> <one-to-many class="VideoGameQuotes.Api.QuoteFlag" />
</set> </set>

View File

@ -82,6 +82,7 @@ namespace VideoGameQuotes.Api {
public virtual int UpVotes { get { return Votes.Count(vote => vote.Direction == VoteDirection.Up); } } public virtual int UpVotes { get { return Votes.Count(vote => vote.Direction == VoteDirection.Up); } }
public virtual int DownVotes { get { return Votes.Count(vote => vote.Direction == VoteDirection.Down); } } public virtual int DownVotes { get { return Votes.Count(vote => vote.Direction == VoteDirection.Down); } }
public virtual int Score { get { return Votes.Sum(vote => (int)vote); } } public virtual int Score { get { return Votes.Sum(vote => (int)vote); } }
public virtual QuoteDto ToDto() { public virtual QuoteDto ToDto() {
return new QuoteDto { return new QuoteDto {
Id = Id, Id = Id,
@ -95,6 +96,14 @@ namespace VideoGameQuotes.Api {
Score = Score Score = Score
}; };
} }
#region nested types
public class GameCriterionHandler : CriterionHandler<Quote> {
protected override Func<Quote, bool> HandleInteger(int value) {
return quote => quote.Game.Id == value;
}
}
#endregion
} }
public class QuoteDto { public class QuoteDto {

View File

@ -30,7 +30,9 @@ namespace VideoGameQuotes.Web.Controllers {
private readonly IDictionary<string, CriterionHandler<Category>> categoryHandlers = new Dictionary<string, CriterionHandler<Category>>(); private readonly IDictionary<string, CriterionHandler<Category>> categoryHandlers = new Dictionary<string, CriterionHandler<Category>>();
private readonly IDictionary<string, CriterionHandler<GamingSystem>> systemHandlers = new Dictionary<string, CriterionHandler<GamingSystem>>(); private readonly IDictionary<string, CriterionHandler<GamingSystem>> systemHandlers = new Dictionary<string, CriterionHandler<GamingSystem>>();
private readonly IDictionary<string, CriterionHandler<Publisher>> publisherHandlers = new Dictionary<string, CriterionHandler<Publisher>>(); private readonly IDictionary<string, CriterionHandler<Publisher>> publisherHandlers = new Dictionary<string, CriterionHandler<Publisher>>();
private readonly IDictionary<string, CriterionHandler<Quote>> quoteHandlers = new Dictionary<string, CriterionHandler<Quote>>(); private readonly IDictionary<string, CriterionHandler<Quote>> quoteHandlers = new Dictionary<string, CriterionHandler<Quote>> {
{ "game", new Quote.GameCriterionHandler() }
};
private readonly IDictionary<string, CriterionHandler<Game>> gameHandlers = new Dictionary<string, CriterionHandler<Game>> { private readonly IDictionary<string, CriterionHandler<Game>> gameHandlers = new Dictionary<string, CriterionHandler<Game>> {
{ "system", new Game.SystemCriterionHandler() }, { "system", new Game.SystemCriterionHandler() },
{ "publisher", new Game.PublisherCriterionHandler() } { "publisher", new Game.PublisherCriterionHandler() }

View File

@ -23,26 +23,11 @@ namespace VideoGameQuotes.Web.Controllers {
} }
public ActionResult Browse(BrowseModel model) { public ActionResult Browse(BrowseModel model) {
var viewModel = new BrowseViewModel(); if (model.IsEmpty) {
if (model.GameIds.Any()) {
viewModel.Games = model.GameIds.Select(id => quoteService.GetGame(id));
}
if (model.SystemIds.Any()) {
viewModel.Systems = model.SystemIds.Select(id => quoteService.GetSystem(id));
}
if (model.PublisherIds.Any()) {
viewModel.Publishers = model.PublisherIds.Select(id => quoteService.GetPublisher(id));
}
if (model.CategoryIds.Any()) {
viewModel.Categories = model.CategoryIds.Select(id => quoteService.GetCategory(id));
}
if (!viewModel.Games.Any() && !viewModel.Systems.Any() && !viewModel.Categories.Any() && !viewModel.Publishers.Any()) {
return View("DefaultBrowse"); return View("DefaultBrowse");
} }
return View("QualifiedBrowse", viewModel); return View("QualifiedBrowse", new QualifiedBrowseModel(model) { Quotes = quoteService.GetBrowsableQuotes(model) });
} }
[HttpPost, IsValidUser] [HttpPost, IsValidUser]

View File

@ -44,7 +44,7 @@ namespace VideoGameQuotes.Web {
routes.IgnoreRoute("media/{*anything}"); routes.IgnoreRoute("media/{*anything}");
routes.MapRoute("api", "api/{action}/{id}/{*criteria}", new { controller = "Api" }, new { action = "game", id = @"\d+|all" }); routes.MapRoute("api", "api/{action}/{id}/{*criteria}", new { controller = "Api" }, new { action = "game|system|category|publisher|quote", id = @"\d+|all" });
routes.MapRoute("home", "{action}", new { controller = "Home", action = "Index" }, new { action = "about|contact" }); routes.MapRoute("home", "{action}", new { controller = "Home", action = "Index" }, new { action = "about|contact" });
@ -53,8 +53,6 @@ namespace VideoGameQuotes.Web {
routes.MapRoute("quote", "{action}", new { controller = "Quote" }, new { action = "submit|search|recent|random|best|vote|report" }); routes.MapRoute("quote", "{action}", new { controller = "Quote" }, new { action = "submit|search|recent|random|best|vote|report" });
routes.MapRoute("individual-quote", "quote/{id}/{*text}", new { controller = "Quote", action = "Quote" }, new { id = @"\d+" }); routes.MapRoute("individual-quote", "quote/{id}/{*text}", new { controller = "Quote", action = "Quote" }, new { id = @"\d+" });
routes.MapRoute("create-category", "category/create", new { controller = "Quote", action = "CreateCategory" }); routes.MapRoute("create-category", "category/create", new { controller = "Quote", action = "CreateCategory" });
routes.MapRoute("Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional });
} }
} }
} }

View File

@ -35,17 +35,14 @@ namespace VideoGameQuotes.Web.Models {
for (var i = 0; i < criteria.Length; i++) { for (var i = 0; i < criteria.Length; i++) {
switch (criteria[i]) { switch (criteria[i]) {
case "system": default:
if (i < criteria.Length - 1) { if (i < criteria.Length - 1) {
model.Criteria["system"] = ParseCommaSeparatedCriterion(criteria[i + 1]); model.Criteria[criteria[i]] = ParseCommaSeparatedCriterion(criteria[i + 1]);
i++; i++;
} else { } else {
controllerContext.AddModelError("criteria", "Unable to parse criteria for key \"system\""); controllerContext.AddModelError("criteria", "Unable to parse criteria for key \"system\"");
} }
break;
default:
controllerContext.AddModelError("criteria", string.Format("Unable to parse criteria for key \"{0}\"", criteria[i]));
break; break;
} }
} }
@ -54,14 +51,17 @@ namespace VideoGameQuotes.Web.Models {
private static IEnumerable<object> ParseCommaSeparatedCriterion(string csv) { private static IEnumerable<object> ParseCommaSeparatedCriterion(string csv) {
var values = csv.Split(','); var values = csv.Split(',');
var results = new List<object>();
foreach (var value in values) { foreach (var value in values) {
int result; int result;
if (int.TryParse(value, out result)) { if (int.TryParse(value, out result)) {
yield return result; results.Add(result);
} else { } else {
yield return value; results.Add(value);
} }
} }
return results;
} }
private static void ParseSort(ControllerContext controllerContext, ApiModel model) { private static void ParseSort(ControllerContext controllerContext, ApiModel model) {

View File

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using VideoGameQuotes.Api;
namespace VideoGameQuotes.Web.Models { namespace VideoGameQuotes.Web.Models {
public class BrowseModel { public class BrowseModel {
@ -10,12 +11,18 @@ namespace VideoGameQuotes.Web.Models {
PublisherIds = Enumerable.Empty<int>(); PublisherIds = Enumerable.Empty<int>();
} }
public User User { get; set; }
public IEnumerable<int> GameIds { get; set; } public IEnumerable<int> GameIds { get; set; }
public IEnumerable<int> SystemIds { get; set; } public IEnumerable<int> SystemIds { get; set; }
public IEnumerable<int> CategoryIds { get; set; } public IEnumerable<int> CategoryIds { get; set; }
public IEnumerable<int> PublisherIds { get; set; } public IEnumerable<int> PublisherIds { get; set; }
public SortMethod SortMethod { get; set; } public SortMethod SortMethod { get; set; }
public SortOrder SortOrder { get; set; } public SortOrder SortOrder { get; set; }
public bool IsEmpty {
get { return !GameIds.Any() && !SystemIds.Any() && !CategoryIds.Any() && !PublisherIds.Any(); }
}
} }
public enum SortMethod { public enum SortMethod {

View File

@ -24,7 +24,7 @@ namespace VideoGameQuotes.Web.Models {
case "category": case "category":
case "publisher": case "publisher":
if (i < segments.Length - 1) { if (i < segments.Length - 1) {
var ids = ParseCommaSeparatedIntegers(segments[i + 1]); var ids = ParseCommaSeparatedIntegers(segments[i + 1]).ToArray(); //nhibernate requires non-iterator
switch (segments[i]) { switch (segments[i]) {
case "game": case "game":
model.GameIds = ids; model.GameIds = ids;

View File

@ -1,19 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using VideoGameQuotes.Api;
namespace VideoGameQuotes.Web.Models {
public class BrowseViewModel {
public BrowseViewModel() {
Games = Enumerable.Empty<Game>();
Systems = Enumerable.Empty<GamingSystem>();
Categories = Enumerable.Empty<Category>();
Publishers = Enumerable.Empty<Publisher>();
}
public IEnumerable<Game> Games { get; set; }
public IEnumerable<GamingSystem> Systems { get; set; }
public IEnumerable<Category> Categories { get; set; }
public IEnumerable<Publisher> Publishers { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using System.Collections.Generic;
using VideoGameQuotes.Api;
namespace VideoGameQuotes.Web.Models {
public class QualifiedBrowseModel : BrowseModel {
public QualifiedBrowseModel(BrowseModel model) {
CategoryIds = model.CategoryIds;
GameIds = model.GameIds;
PublisherIds = model.PublisherIds;
SortMethod = model.SortMethod;
SystemIds = model.SystemIds;
SortOrder = model.SortOrder;
}
public IEnumerable<Quote> Quotes { get; set; }
}
}

View File

@ -4,6 +4,7 @@ using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using Portoa.Persistence; using Portoa.Persistence;
using VideoGameQuotes.Api; using VideoGameQuotes.Api;
using VideoGameQuotes.Web.Models;
namespace VideoGameQuotes.Web.Services { namespace VideoGameQuotes.Web.Services {
public interface IQuoteService { public interface IQuoteService {
@ -24,6 +25,7 @@ namespace VideoGameQuotes.Web.Services {
IEnumerable<Quote> GetBestQuotes(int start, int end); IEnumerable<Quote> GetBestQuotes(int start, int end);
Vote SaveVote(Vote vote); Vote SaveVote(Vote vote);
Vote GetVoteOrCreateNew(Quote quote, User voter); Vote GetVoteOrCreateNew(Quote quote, User voter);
IEnumerable<Quote> GetBrowsableQuotes(BrowseModel model);
} }
public class QuoteService : IQuoteService { public class QuoteService : IQuoteService {
@ -147,5 +149,24 @@ namespace VideoGameQuotes.Web.Services {
Voter = voter Voter = voter
}; };
} }
[UnitOfWork]
public IEnumerable<Quote> GetBrowsableQuotes(BrowseModel model) {
var quotes = quoteRepository.Records;
if (model.GameIds.Any()) {
quotes = quotes.Where(quote => model.GameIds.Contains(quote.Game.Id));
}
if (model.PublisherIds.Any()) {
quotes = quotes.Where(quote => quote.Game.Publishers.Any(publisher => model.PublisherIds.Contains(publisher.Id)));
}
if (model.SystemIds.Any()) {
quotes = quotes.Where(quote => quote.Game.Systems.Any(system => model.SystemIds.Contains(system.Id)));
}
if (model.CategoryIds.Any()) {
quotes = quotes.Where(quote => quote.Categories.Any(category => model.CategoryIds.Contains(category.Id)));
}
return quotes;
}
} }
} }

View File

@ -89,7 +89,7 @@
<Compile Include="Models\ApiModel.cs" /> <Compile Include="Models\ApiModel.cs" />
<Compile Include="Models\BrowseModel.cs" /> <Compile Include="Models\BrowseModel.cs" />
<Compile Include="Models\BrowseModelBinder.cs" /> <Compile Include="Models\BrowseModelBinder.cs" />
<Compile Include="Models\BrowseViewModel.cs" /> <Compile Include="Models\QualifiedBrowseModel.cs" />
<Compile Include="Models\PagedQuoteCollectionModel.cs" /> <Compile Include="Models\PagedQuoteCollectionModel.cs" />
<Compile Include="Models\QuoteCollectionModel.cs" /> <Compile Include="Models\QuoteCollectionModel.cs" />
<Compile Include="Models\QuoteModel.cs" /> <Compile Include="Models\QuoteModel.cs" />

View File

@ -63,6 +63,17 @@
}; };
}(); }();
var systemCellRenderer = function() {
var $template = $("<a/>");
return function(system, count) {
return $template
.clone()
.attr("href", "/browse/system/" + system.Id)
.attr("title", system.Name)
.text(system.Abbreviation);
};
}();
$("#show-default-menu").click(function() { $("#show-default-menu").click(function() {
$content.empty(); $content.empty();
$container.hide(); $container.hide();
@ -94,6 +105,31 @@
return false; return false;
}); });
$("#browse-system").click(function() {
$browseMenu.hide();
$container.show();
var cellsPerRow = 12;
if (systems.length === 0) {
$.ajax("/api/system/all", {
data: { sort: "alphabetical" },
success: function(data, status, $xhr) {
if (data.Error !== null) {
alert(data.Error);
return;
}
systems = data.Data.systems;
renderData(systems, systemCellRenderer, cellsPerRow);
}
});
} else {
renderData(systems, systemCellRenderer, cellsPerRow);
}
return false;
});
}); });
//]]></script> //]]></script>
</asp:Content> </asp:Content>

View File

@ -1,8 +1,15 @@
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<VideoGameQuotes.Web.Models.BrowseViewModel>" MasterPageFile="~/Views/Shared/Site.Master" %> <%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<VideoGameQuotes.Web.Models.QualifiedBrowseModel>" MasterPageFile="~/Views/Shared/Site.Master" %>
<%@ Import Namespace="VideoGameQuotes.Web.Models" %>
<asp:Content runat="server" ID="Title" ContentPlaceHolderID="TitleContent">Browse</asp:Content> <asp:Content runat="server" ID="Title" ContentPlaceHolderID="TitleContent">Browse</asp:Content>
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent"> <asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
<p> <p>
This is the qualified browse view. Here are some quotes
</p> </p>
<%
foreach (var quote in Model.Quotes) {
Html.RenderPartial("SingleQuote", new QuoteModel { Quote = quote, User = Model.User });
}
%>
</asp:Content> </asp:Content>

View File

@ -122,6 +122,12 @@ ul.menu li {
#browse-default-container { #browse-default-container {
display: none; display: none;
} }
#browse-default-content table {
width: 100%;
}
#browse-default-content td {
padding: 5px;
}
#header { #header {
background-color: #669966; background-color: #669966;