... от нашего старого
окружения, где настройки ...
Одной из возможностей повышения качества выпускаемого продукта является соответствие окружений на боевых серверах и в среде тестирования. Мы постарались минимизировать количество ошибок, связанных с различием конфигураций, путем перехода от нашего старого тестового окружения, где настройки сервисов сильно отличались от боевых, к новому окружению, где конфигурация практически соответствует боевой. Сделали мы это с помощью docker и ansible, получили много профита, но и не избежали различных проблем. Об этом переходе и интересных подробностях я постараюсь рассказать в данной статье.
Читать дальше →
... идеальные варианты настройки
веб-севера, хотя ...
Вполнить и прислать проект Visual Studio 2013, обладающий следующим ...
Задание:
Вполнить и прислать проект Visual Studio 2013, обладающий следующим функционалом:
1. В проекте должна быть web-форма для пользовательского ввода, позволяющая вести
данные о клиенте:
- фамилия, имя отчество;
- номер телефона для связи;
- номер паспорта;
- кем выдан;
- адрес прописки.
2. По копке «сформировать документ» данные из формы должны быть переданы в документ MS Word примерного вида:
Соглашение
Я, Иванов И.И., подтверждаю, что паспортные дынные, указвнные мною в анкете , а именнно:
паспорт № ХХХХ ХХХХХХ, выдан ОВД по району Камышинский города Ухты 12.12.1950 года сответствуют действительности.
Прошу связываться со мной посредством почты по адресу 100001, г. Ухта, ул Ленина, дом 1, квартира 100.
Номер контактного телефона (456) 123 45-67.
Дата, Подпись.
3. Должен вестись лог формирования документов вида:
- Дата формирования документа
- Время формирования документа
- Фамилия клиента
По заданию приветствуется использование валидации пользовательского ввода, XML и Dataset.
Предположительное время реализация – два дня.
Небольшие пояснения - как делал:
Остальное тривиально, немного про формирование документа напишу. Оно происходит так:
1) Данные из формы попадают в класс модели
2) Формируется шаблон HTML с razor разметкой для данных модели.
3) Вызывая razor парсер из библиотеки RazorEngine (ставится из Nuget) формируем окончательный html
4) Пишем html в temp каталог
5) Конвертим с помощью HTMLConverter.exe HTML в DOC
6) Грузим данные данные DOC файла в байтовый массив
7) Возвращаем данные doc файла как результат веб запроса указав MIME тип.
Для того чтобы заработало нужно разместить в C:/temp файл HTMLConverter.exe из папки Html2DocConverter.
Инсталятор конвертера лежит в папке Html2DocConverterDistributive.
Возможно его потребуется поставить. Конвертер используется для преобразования
из html в doc вызовом консольного приложения.
В той же папке будут созданны промежуточные файлы, которые при каждом запуске будут удаляться.
Там же будет лежать файл addclientlog.xml с xml логом добавления клиентов.
Xml не совсем коррестный без заголовков, но такие нормально
программно парсятся, а писать в его не в пример проще.
Если хочется поменять каталог с временными файлами и логом, то смените константу _TempCatalogPath
в классе BusinessLogic.
Исходник ClientsData.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
namespace MielTestTask.Models
{
public class ClientsData
{
[Required(ErrorMessage = "Поле ввода ФИО должно быть заполнено")]
[DisplayFormat(ConvertEmptyStringToNull = false)]
[RegularExpression(@"([А-ЯЁ][а-яё]+[\-\s]?){3,}",
ErrorMessage = "Формат ввода ФИО должен быть таким: Фамилия Имя Отчество")]
[Display(Name = "Фамилия Имя Отчество")]
public String FIO { get; set; }
[Required(ErrorMessage = "Поле ввода телефона должно быть заполнено")]
[DisplayFormat(ConvertEmptyStringToNull = false)]
[RegularExpression(@"\+?\d+([\(\s\-]?\d+[\)\s\-]?[\d\s\-]+)?",
ErrorMessage = "Формат ввода ФИО должен быть таким: Фамилия Имя Отчество")]
[Display(Name = "Телефон")]
public String Phone { get; set; }
[Required(ErrorMessage = "Поле ввода номера паспорта должно быть заполнено")]
[DisplayFormat(ConvertEmptyStringToNull = false)]
[RegularExpression(@"\d{4}\s\d{6}",
ErrorMessage = "Формат ввода номера пасспорта должен быть таким: dddd dddddd")]
[Display(Name = "Номер паспорта")]
public String Passport { get; set; }
[Required(ErrorMessage = "Поле ввода кем выдан должно быть заполнено")]
[DisplayFormat(ConvertEmptyStringToNull = false)]
[StringLength(200,
ErrorMessage = "Длина текста поля кем выдан не должна превышать 200 символов")]
[Display(Name = "Кем выдан")]
public String WhomIssued { get; set; }
[Required(ErrorMessage = "Поле ввода адрес должно быть заполнено")]
[DisplayFormat(ConvertEmptyStringToNull = false)]
[StringLength(200,
ErrorMessage = "Длина текста поля адрес не должна превышать 200 символов")]
[Display(Name = "Адрес")]
public String Address { get; set; }
}
}
Исходник Index.cshtml
@model MielTestTask.Models.ClientsData
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
@using (Html.BeginForm("ProduceDocument", "Page"))
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
<fieldset style="width:170px;" >
<legend>Данные клиента</legend>
<div class="editor-label">
@Html.LabelFor(model => model.FIO)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.FIO)
@Html.ValidationMessageFor(model => model.FIO)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Phone)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Phone)
@Html.ValidationMessageFor(model => model.Phone)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Passport)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Passport)
@Html.ValidationMessageFor(model => model.Passport)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.WhomIssued)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.WhomIssued)
@Html.ValidationMessageFor(model => model.WhomIssued)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Address)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Address)
@Html.ValidationMessageFor(model => model.Address)
</div>
<p>
<input type="submit" value="Сформировать документ" />
</p>
</fieldset>
}
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
Исходник PageController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.IO;
using MielTestTask.Models;
using MielTestTask.App_Code;
namespace MielTestTask.Controllers
{
public class PageController : Controller
{
public ActionResult Index()
{
ClientsData data = new ClientsData();
return View(data);
}
[HttpPost]
public ActionResult ProduceDocument(ClientsData cData)
{
BusinessLogic.DeleteTempFiles();
BusinessLogic.ClientsDataSave2Html(cData);
BusinessLogic.Html2MSWordDocFile();
Byte[] docFileData = BusinessLogic.GetDocFileData();
BusinessLogic.SaveLogItem(cData);
FileContentResult currResult = File(docFileData,
MimeMapping.GetMimeMapping(BusinessLogic.TempDocFileName),
BusinessLogic.TempDocFileName
);
return currResult;
}
}
}
Исходник BusinessLogic.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using System.Diagnostics;
using System.ComponentModel.DataAnnotations;
using System.Xml.Linq;
using RazorEngine;
using MielTestTask.Models;
namespace MielTestTask.App_Code
{
public static class BusinessLogic
{
private const String _TempCatalogPath = "C:/temp/";
private const String _TempHTMLFileName = _TempCatalogPath + "temp.html";
public const String TempDocFileName = _TempCatalogPath + "temp.doc";
private const String _LogFileName = _TempCatalogPath + "addclientlog.xml";
private const String _ConverterPathName = _TempCatalogPath + "HTMLConverter.exe";
public static String GenerateShortName(String fio)
{
String[] arrStr = fio.Split( new Char[] { ' ' } );
String Surname = arrStr[0];
String Name = arrStr[1];
String Patronomic = arrStr[2];
String Result = Surname + " " + Name[0] + ". " + Patronomic[0] + ".";
return Result;
}
public static void DeleteTempFiles()
{
try
{
File.Delete(_TempHTMLFileName);
File.Delete(TempDocFileName);
}
catch
{
; // гасим возможные исключения
}
}
public static void ClientsDataSave2Html(ClientsData cData)
{
String shortName = GenerateShortName(cData.FIO);
String currHtml =
" <html> " +
" <head> " +
" <style type=\"text/css\"> " +
" .lbl { color:blue; } " +
" </style> " +
" <meta charset=\"utf-8\" /> " +
" </head> " +
" <body> " +
" <P style=\"text-align:center;\" >" +
" Соглашение <BR>" +
" </P>" +
" <BR>" +
" Я, <span class=\"lbl\">" + shortName + "</span>,
подтверждаю, что паспортные дынные, указвнные мною в анкете , а именнно: " +
" паспорт <span class=\"lbl\">№@Model.Passport</span> ,
выдан <span class=\"lbl\">@Model.WhomIssued</span>
сответствуют действительности. <BR>" +
" Прошу связываться со мной посредством почты по адресу
<span class=\"lbl\">@Model.Address</span>. <BR>" +
" Номер контактного телефона <span class=\"lbl\">
@Model.Phone</span>. <BR>" +
" <P style=\"text-align:right;\" >" +
" " +
DateTime.Now.ToString() + ",_____________ " +
" </P>" +
" </body> " +
" </html> ";
String resultHtml = Razor.Parse(currHtml, cData);
File.WriteAllText(_TempHTMLFileName, resultHtml);
}
public static void Html2MSWordDocFile()
{
var process = new Process
{
StartInfo =
{
FileName = _ConverterPathName,
Arguments = _TempHTMLFileName + " " + TempDocFileName
}
};
process.Start();
}
public static Byte[] GetDocFileData()
{
Byte[] result = File.ReadAllBytes(_TempHTMLFileName );
return result;
}
public static void SaveLogItem(ClientsData cData)
{
String surname = cData.FIO.Split(new Char[] { ' ' })[0];
DateTime currDt = DateTime.Now;
XElement currEl = new XElement
(
"LogItem",
new XAttribute("Date", currDt.ToString("dd/mm/yyyy")),
new XAttribute("Time", currDt.ToString("HH:mm")),
new XAttribute("ClientSurname", surname)
);
String logItemStr = currEl.ToString() + "\n";
File.AppendAllText(_LogFileName, logItemStr);
}
}
}
Солюшн в архиве (без Html2DocConverter): TestTask.zip (12,42 mb)
... расскажу о создании
локального блога. Прошло ...
Открытое бета-те ...
Открытое бета-тестирование
Hearthstone: Heroes of Warcraft идет уже не первый месяц, и вчера закончился третий тестовый сезон. Список лучших игроков в европейском регионе можно посмотреть на официальном сайте.
Однако на этом тестирование не заканчивается, и уже начался четвертый тестовый сезон. А это значит, что за наград за ранговую игру придется подождать еще не меньше месяца.
Тем временем
Blizzard показала примеры наград, которые можно будет получить в том числе и за ранговую игру — это альтернативные рубашки карт:
Первую из этих рубашек можно получить за «специальные события» в игре. Вторую — за достижение легендарного ранга в режиме ранговой игры. Четвертую дадут просто за участие в ранговой игре. Однако награды эти будут только после окончательного релиза игры и начала настоящих сезонов, а не тестовых.
Также на официальном сайте появилась первая из серии статей о создании визуального стиля Hearthstone.
Если же вас интересует история героев из вселенной Warcraft, за которых мы играем в HS, то читайте статьи о них на официальном сайте:
- Охотник Рексар
- Чернокнижник Гул'дан
- Разбойница Валира
- Жрец Андуин Ринн
- Шаман Тралл
Continue reading
→