Introduction
In this post, I want to present one way of creating interactive menu based on specific user activity. The main idea is to show suitable menu for each user based on his previous actions. For this, I created a test ASP.NET MVC 3 application. It emulates the "Football News" web site where user can select a country to see its championship news. See the main menu below:
Presentation
As I said, each user would have appropriate menu based on his actions. Let's look at them.
The first one is Pedro Duarte. He is from Spain and usually he clicks on "Spain" menu item, but also he is interested in what happens in England Championship. Sometimes, if the Spain and England news has been read, he visit German, Italy, and France Championships. Thus, if Pedro Duarte goes to the website, he see the next menu:
Next user is Cole Jones. He is from England, therefore the most visited menu item is England Championship. Also, he usually visits Spain and Germany and sometimes Italy and France.
Daniel Weber is from Germany:
Antonio Rossi is from Italy:
Lucas Martin is from France:
So, the above pictures demonstrate the interactive menus that are presented in suitable view for each user. The user doesn't have to find this favorite Championship reading each menu item. He intuitively would know that his favorite items are emphasized and are on the top(left) of the list.
Using the Code
The data is stored in SDF database. For data manipulation, I use Entity Framework. The database structure is described below.
User table:
[Table("Users")]
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[MaxLength(50)]
public string Name { get; set; }
}
Visit table:
[Table("Visits")]
public class Visit
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[MaxLength(50)]
public string ContentId { get; set; }
public int UserId { get; set; }
}
Database context:
public class FanatContext : DbContext
{
public FanatContext()
: base("FanatConnection")
{}
public DbSet<User> Users { get; set; }
public DbSet<Visit> Visits { get; set; }
}
Connection string in web.config:
<add name="FanatConnection"
connectionString="Data Source=|DataDirectory|Fanat.sdf"
providerName="System.Data.SqlServerCe.4.0"/>
Next class initializes the SDF database:
public class FanatContextInitializer : CreateDatabaseIfNotExists<FanatContext>
{
protected override void Seed(FanatContext context)
{
var userSpain = new User() { Name = "Pedro Duarte" };
var userEngland = new User() { Name = "Cole Jones" };
var userGerman = new User() { Name = "Daniel Weber" };
var userItaly = new User() { Name = "Antonio Rossi" };
var userFrance = new User() { Name = "Lucas Martin" };
context.Users.Add(userSpain);
context.Users.Add(userEngland);
context.Users.Add(userGerman);
context.Users.Add(userItaly);
context.Users.Add(userFrance);
context.SaveChanges();
InitVisits(context, userSpain.Id, 100, 50, 20, 10, 5);
InitVisits(context, userEngland.Id, 70, 150, 55, 30, 18);
InitVisits(context, userGerman.Id, 42, 22, 110, 45, 10);
InitVisits(context, userItaly.Id, 132, 29, 15, 200, 20);
InitVisits(context, userFrance.Id, 69, 65, 50, 15, 70);
base.Seed(context);
}
private static void InitVisits(FanatContext context, int userId,
int spainVisits, int englandVisits, int germanVisits, int italyVisits, int franceVisits)
{
InitVisits(context, userId, ContenIds.Spain, spainVisits);
InitVisits(context, userId, ContenIds.England, englandVisits);
InitVisits(context, userId, ContenIds.Germany, germanVisits);
InitVisits(context, userId, ContenIds.Italy, italyVisits);
InitVisits(context, userId, ContenIds.France, franceVisits);
}
private static void InitVisits(FanatContext context, int userId, string contentId, int count)
{
if (count > 0)
{
for (int i = 0; i < count; i++)
{
var visit = new Visit()
{
UserId = userId,
ContentId = contentId
};
context.Visits.Add(visit);
}
context.SaveChanges();
}
}
}
Controller has one view action - Index
. It returns menu model based on user information. Menu items are ordered by rate value. This value is equal to visits by user.
public ActionResult Index()
{
var menuMolel = new MenuModel();
var userId = 0;
int.TryParse(Request.Params["u"], out userId);
menuMolel.SelectedId = userId;
var items = new List<MenuItem>();
items.Add(new MenuItem("Spain", ContenIds.Spain, GetVisits(userId, ContenIds.Spain)));
items.Add(new MenuItem("England", ContenIds.England, GetVisits(userId, ContenIds.England)));
items.Add(new MenuItem("Germany", ContenIds.Germany, GetVisits(userId, ContenIds.Germany)));
items.Add(new MenuItem("Italy", ContenIds.Italy, GetVisits(userId, ContenIds.Italy)));
items.Add(new MenuItem("France", ContenIds.France, GetVisits(userId, ContenIds.France)));
menuMolel.Items.AddRange(items.OrderByDescending(x => x.Rate));
menuMolel.Users.AddRange(GetUsers());
return View(menuMolel);
}
private int GetVisits(int userId, string contentId)
{
using (var context = new FanatContext())
{
return context.Visits.Where(x => x.UserId == userId && x.ContentId == contentId).Count();
}
}
private IEnumerable<User> GetUsers()
{
using (var context = new FanatContext())
{
return context.Users.ToList();
}
}
MenuItem
and MenuMode
classes:
public class MenuItem
{
public MenuItem()
{}
public MenuItem(string name, string contentId, double rate = 0)
{
this.Name = name;
this.ContentId = contentId;
this.Rate = rate;
}
public string Name { get; set; }
public string ContentId { get; set; }
public double Rate { get; set; }
}
public class MenuModel
{
public MenuModel()
{
Items = new List<MenuItem>();
Users = new List<User>();
}
public List<MenuItem> Items { get; set; }
public List<User> Users { get; set; }
public int SelectedId { get; set; }
}
The menu items are ordered on the server but what about CSS emphasizing? Here, I'm using I2UI JavaScript library:
<script src="http://i2ui.com/Scripts/Downloads/i2ui-1.0.0.js" type="text/javascript"></script>
To emphasize HTML tags, you need to add additional attribute "data-i2
":
<div class="menu clear-fix" data-i2="css:['.from-item','.to-item']">
@foreach (var item in Model.Items)
{
<div class="item" data-i2="rate:@item.Rate">@item.Name</div>
}
</div>
Here, the div
with "menu
" class takes "data-i2
" attribute with the CSS selectors range: 'from-item
' and 'to-item
'. The inner div
with "item
" class takes the "data-i2
" attribute with the rate value.
CSS range:
.from-item
{
font-size: 12px;
padding: 3px;
margin: 3px;
opacity: 0.5;
border-radius: 3px;
background-color: #649151;
color: #fff;
}
.to-item
{
font-size: 30px;
padding: 10px;
margin: 10px;
opacity: 1;
border-radius: 10px;
background-color: #164C00;
color: #fff;
}
Also, you need to call emphasize function in JavaScript:
$(document).ready(function () {
i2.emph();
});
Requirements
Visual Studio 2010
Need to have installed:
- ASP.NET MVC 3
- Microsoft SQL Server Compact 4.0
- Entity Framework 4