ASP.NET MVC: История одного проекта Облако тегов (часть 11)

Содержание

ASP.NET MVC: История одного проекта «Готовимся к старту» (часть 1)
ASP.NET MVC: История одного проекта «Всё ради данных» (часть 2)
ASP.NET MVC: История одного проекта «Шаблоны и внешний вид» (часть 3)
ASP.NET MVC: История одного проекта «Еще немного классов» (часть 4)
ASP.NET MVC: История одного проекта «UI — всё для пользователя» (часть 5)
ASP.NET MVC: История одного проекта «UI — Добавление экспоната» (часть 6)
ASP.NET MVC: История одного проекта «UI — Редактирование экспоната» (часть 7)
ASP.NET MVC: История одного проекта «Обработка ошибок» (часть 8)
ASP.NET MVC: История одного проекта «Фильтрация» (часть 9)
ASP.NET MVC: История одного проекта «Поиск» (часть 10)
ASP.NET MVC: История одного проекта «Облако тегов» (часть 11)
ASP.NET MVC: История одного проекта «Главная страница» (часть 12)

Тема дня

Облако тегов очень удобный и распространный вариант навигации по сайту. Тема этой части — «Облако меток» (или облако тегов, если хотите, или вообще, облако ключевых слов).

Предмет разговора

Экспонаты в музее юмора помечаются метками, а значит эти метки можно представить пользователю в виде облака. Облако буду делать с разбиением на группы (кластеры), чтобы оно наглядно отображало частоту использования меток, отображая часто используемы большим размеров, а малоспользуемые, соответственно, меньшим.

Кластерный анализ

Итак, приступим. У меня на сайте в музее очень большое количество меток. Для того чтобы сформировать «правильное» облако меток, воспользуюсь методом кластерного анализа. Этот алгоритм достаточно прост, и для достижения успешного результата надо:

Описаный процесс можно узреть на блок-схеме:

Я определю в своем облаке 10 групп (кластеров). У меня кластеры (группы) будут отображаться разными CSS стилями. Таким образом, у меня будет для каждой из групп задан свой стиль в CSS. Определения стилей показывать не буду, пусть каждый раскрасит метки в зависимости от «частоты» самостоятельно.

Класс TagCloud

Для начала создаю класс, который будет использоваться при формировании облака меток:

/// <summary> /// Класс тега для облака. /// </summary> public class TagCloud { #region конструкторы public TagCloud() { } public TagCloud(string name, string css, int total) { this.Name = name; this.CssClass = css; this.Total = total; } public TagCloud(int id, string name, string css, int total) { this.Id = id; this.Name = name; this.CssClass = css; this.Total = total; } #endregion #region свойства /// <summary> /// Идентификатор /// </summary> public int Id { get; set; } /// <summary> /// Наименование /// </summary> public string Name { get; set; } /// <summary> /// Стиль Css /// </summary> public string CssClass { get; set; } /// <summary> /// Всего изпользован /// </summary> public int Total { get; set; } #endregion }

Формирование облака

Надеюсь всё понятно, со свойствами и с конструкторами класса TagCloud. Теперь надо сделать расширение (extension), которое будет метки (tag) превращать в метку для облака (TagCloud):

internal static IEnumerable<TagCloud> TagsToCloudItems(this IEnumerable<Tag> source) { if (source == null) throw new ArgumentNullException(«source»); return source.Select(x => new TagCloud(x.Id, x.Name, «tag», x.Exhibits.Count)); }

Следующим этапом требуется создать расширение (extension), которое разобъёт сформированные метки для облака (TagCloud) по группам (в кластеры). Приведу этот код целиком:

/// <summary> /// Создает облако тегов /// </summary> /// <param name=»tagsCloud»>Массив меток</param> /// <param name=»clusterCount»>Количество кластеров при генерации</param> internal static IEnumerable<TagCloud> CreatorCloud(this IEnumerable<TagCloud> tagsCloud, int ClusterCount) { int totalCount = tagsCloud.Count(); tagsCloud = tagsCloud.OrderBy(ff => ff.Total).ToArray(); List<List<TagCloud>> clusters = new List<List<TagCloud>>(); if (totalCount > 0) { int min = tagsCloud.Min(c => c.Total); int max = tagsCloud.Max(c => c.Total) + min; int completeRange = max — min; double groupRange = (double)completeRange / (double)(ClusterCount); List<TagCloud> cluster = new List<TagCloud>(); double currentRange = min + groupRange; for (int i = 0; i < totalCount; i++) { while (tagsCloud.ToArray()[i].Total > currentRange) { clusters.Add(cluster); cluster = new List<TagCloud>(); currentRange += groupRange; } cluster.Add(tagsCloud.ToArray()[i]); } clusters.Add(cluster); } TagCloud tc; List<TagCloud> result = new List<TagCloud>(); for (int i = 0; i < clusters.Count; i++) { foreach (TagCloud item in clusters[i]) { tc = new TagCloud(item.Id, item.Name, «tag» + i.ToString(), item.Total); result.Add(tc); } } return result.OrderBy(x => x.Name).AsEnumerable(); }

Можно было бы сделать всё в одном методе: и превращение меток в облачные метки, и сразу же разбросать их по группам. Но я специально разделил на два метода. Первый будет использоваться в WCF-сервисе для Silverlight. Далее в контролере Museum создаем новый ActionResult:

[OutputCache(Duration = 2880)] public ActionResult Cloud() { var model = tagRepository .AllIncluding(x => x.Exhibits) .TagsToCloudItems() .CreateCloud(10); return View(model); }

Просто последовательно вызываю два расширения на коллекцию меток (Tag), и затем отдаю их в представление (View). Обратите внимание на то, что я пометил данный метод аттрибутом OutputCache, который на 48 часов будет кэшировать данные выдаваемые в результате выполнения этого метода. Это сделано в силу того, что частота обновления облака не велика. 

UI облака

Если есть метод Cloud значить должно быть и представление (View). В этом представлении подключаю дополнительный CSS для отображения меток по группам кластера, о котором  я говорил выше. А отрисовку меток делает UserControl (выделено жирным):

@section header{ <link href=»@Url.Content(«/content/tags.css»)» rel=»stylesheet» type=»text/css» /> } @model IEnumerable<Calabonga.Mvc.Humor.Models.TagCloud>     @{ ViewBag.Title = «Облоко тегов»; Layout = «~/Views/Shared/_LayoutMain.cshtml»; } <h2> Облако меток музейных экспонатов</h2> <p>@Html.Partial(«Controls/TagCloudControl»)</p>

И сам контрол для отрисовки меток:

@model IEnumerable<Calabonga.Mvc.Humor.Models.TagCloud> @if (Model != null && Model.Count() > 0) { foreach (Calabonga.Mvc.Humor.Models.TagCloud item in Model) { <span style=»margin: 2px; padding: 2px; line-height: 2em; background-color: #f5f5f5;»> @Html.ActionLink( string.Concat(item.Name, » («, @item.Total, «)»),  «index»,  «museum»,  new { t = item.Name },  new { @class = item.CssClass })   </span> } }

Запускаем… Хм… Ссылки-то на облако меток нет… Облако меток у меня есть только в музее юмора, а на ленте быть не должно. Значить достаточно разместить ссылку на некоторых страницах музея юмора (но не ленты экспонатов, чтобы не вводить в заблуждение посетителей). Я разместил на странице отображения всех экспонатов музея и на детальном просмотре. А теперь нажмем…

Метки кликабельны, а значит получилось то, что и требовалось.

Заключение

Вы можете скачать текущую версию проекта, если у вас что-то не получилось или что-то не понятно. А мне осталось сказать: «спасибо за внимание».

Подробнее: http://feedproxy.google.com/~r/blogmusor/~3/Zf3dmc9ghKs/86

Источник: lred.ru

Оцените статью
новости для мужчин