Загрузка данных


# -*- coding: utf-8 -*-
import os
import glob

root = os.path.dirname(os.path.abspath(__file__))

FILES = {}

FILES['Global.asax'] = r'''
<%@ Application Codebehind="Global.asax.cs" Inherits="WebApplication3.Global" Language="C#" %>
'''

FILES['Global.asax.cs'] = r'''
using System;
using System.Web.Routing;

namespace WebApplication3
{
    public class Global : System.Web.HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }
    }
}
'''

FILES['RouteConfig.cs'] = r'''
using System.Web.Routing;

namespace WebApplication3
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.MapPageRoute("NewProductRoute", "products/new", "~/NewProduct.aspx");

            routes.MapPageRoute("ProductsListRoute", "products", "~/Products.aspx");
            routes.MapPageRoute("ProductsRoute", "products/{id}", "~/Product.aspx",
                false, null,
                new RouteValueDictionary { { "id", @"^\d+$" } });

            routes.MapPageRoute("StudentsRoute", "students", "~/Student.aspx");
            routes.MapPageRoute("StudentRoute", "students/{id}", "~/Student.aspx",
                false, null,
                new RouteValueDictionary { { "id", @"^[1-9]\d*$" } });

            routes.MapPageRoute("CatalogRoute", "catalog/{category}", "~/Catalog.aspx");
            routes.MapPageRoute("CatalogItemRoute", "catalog/{category}/{id}", "~/Catalog.aspx",
                false, null,
                new RouteValueDictionary { { "id", @"^\d+$" } });

            routes.MapPageRoute("ArchiveRoute", "archive/{year}/{month}", "~/Archive.aspx",
                false, null,
                new RouteValueDictionary
                {
                    { "year", @"^\d{4}$" },
                    { "month", @"^(0[1-9]|1[0-2])$" }
                });

            routes.MapPageRoute("BooksRoute", "books", "~/Books.aspx");

            routes.MapPageRoute("BooksPageRoute", "books/page/{page}", "~/Books.aspx",
                false,
                new RouteValueDictionary { { "page", "1" } },
                new RouteValueDictionary { { "page", @"^\d+$" } });

            routes.MapPageRoute("BookRoute", "books/{id}", "~/Book.aspx",
                false, null,
                new RouteValueDictionary { { "id", @"^\d+$" } });
            routes.MapPageRoute("AuthorRoute", "authors/{name}", "~/Author.aspx");
            routes.MapPageRoute("GenreRoute", "genres/{name}", "~/Genre.aspx");

            routes.MapPageRoute("SearchRoute", "search/{text}", "~/Search.aspx");
            routes.MapPageRoute("AuthorBookRoute", "authors/{name}/books/{id}", "~/Book.aspx",
                false, null,
                new RouteValueDictionary { { "id", @"^\d+$" } });

            routes.MapPageRoute("RouteDebugRoute", "routedebug", "~/RouteDebug.aspx");
        }
    }
}
'''

FILES['Data.cs'] = r'''
using System.Collections.Generic;
using System.Linq;

namespace WebApplication3
{
    public class ProductItem
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Category { get; set; }
        public decimal Price { get; set; }
        public string Description { get; set; }
    }

    public static class Shop
    {
        public static readonly List<ProductItem> Products = new List<ProductItem>
        {
            new ProductItem { Id = 1, Name = "Ноутбук Lenovo IdeaPad 3",      Category = "laptops", Price = 45990m, Description = "15.6\", Ryzen 5, 16 ГБ ОЗУ, SSD 512 ГБ." },
            new ProductItem { Id = 2, Name = "Ноутбук ASUS TUF Gaming",       Category = "laptops", Price = 79990m, Description = "Игровой ноутбук, RTX 3050, экран 144 Гц." },
            new ProductItem { Id = 3, Name = "Ноутбук HP Pavilion 15",        Category = "laptops", Price = 54990m, Description = "15.6\", Core i5, 8 ГБ ОЗУ, SSD 256 ГБ." },
            new ProductItem { Id = 4, Name = "Смартфон Samsung Galaxy A54",   Category = "phones",  Price = 32990m, Description = "6.4\", 8/128 ГБ, камера 50 Мп." },
            new ProductItem { Id = 5, Name = "Смартфон Xiaomi Redmi Note 13", Category = "phones",  Price = 19990m, Description = "6.6\", 8/256 ГБ, AMOLED 120 Гц." },
            new ProductItem { Id = 6, Name = "Смартфон iPhone 15",            Category = "phones",  Price = 89990m, Description = "6.1\", процессор A16, 128 ГБ." },
            new ProductItem { Id = 7, Name = "Планшет Samsung Galaxy Tab S9", Category = "tablets", Price = 64990m, Description = "11\", 8/128 ГБ, стилус S Pen." },
            new ProductItem { Id = 8, Name = "Планшет Apple iPad 10.2",       Category = "tablets", Price = 37990m, Description = "10.2\", A13, 64 ГБ." },
            new ProductItem { Id = 9, Name = "Планшет Huawei MatePad SE",     Category = "tablets", Price = 16990m, Description = "10.4\", 4/128 ГБ." }
        };

        public static readonly List<string> Categories = new List<string> { "laptops", "phones", "tablets" };

        public static ProductItem Find(int id)
        {
            return Products.FirstOrDefault(p => p.Id == id);
        }
    }

    public class BookItem
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Author { get; set; }
        public string Genre { get; set; }
        public int Year { get; set; }
        public string Description { get; set; }
    }

    public static class Library
    {
        public static readonly List<BookItem> Books = new List<BookItem>
        {
            new BookItem { Id = 1,  Title = "Мастер и Маргарита",       Author = "Михаил Булгаков",   Genre = "Роман",          Year = 1967, Description = "Мистический роман о визите дьявола в Москву." },
            new BookItem { Id = 2,  Title = "Собачье сердце",           Author = "Михаил Булгаков",   Genre = "Повесть",        Year = 1925, Description = "История превращения собаки в человека." },
            new BookItem { Id = 3,  Title = "Преступление и наказание", Author = "Фёдор Достоевский", Genre = "Роман",          Year = 1866, Description = "Роман о студенте, переступившем нравственный закон." },
            new BookItem { Id = 4,  Title = "Идиот",                    Author = "Фёдор Достоевский", Genre = "Роман",          Year = 1869, Description = "История князя Мышкина." },
            new BookItem { Id = 5,  Title = "Война и мир",              Author = "Лев Толстой",       Genre = "Роман",          Year = 1869, Description = "Эпопея о судьбах дворянства в эпоху наполеоновских войн." },
            new BookItem { Id = 6,  Title = "Анна Каренина",            Author = "Лев Толстой",       Genre = "Роман",          Year = 1878, Description = "Трагедия любви Анны и Вронского." },
            new BookItem { Id = 7,  Title = "Евгений Онегин",           Author = "Александр Пушкин",  Genre = "Роман в стихах", Year = 1833, Description = "Роман в стихах о «лишнем человеке»." },
            new BookItem { Id = 8,  Title = "Капитанская дочка",        Author = "Александр Пушкин",  Genre = "Повесть",        Year = 1836, Description = "Повесть о Пугачёвском восстании." },
            new BookItem { Id = 9,  Title = "Тихий Дон",                Author = "Михаил Шолохов",    Genre = "Роман",          Year = 1940, Description = "Эпопея о донском казачестве." },
            new BookItem { Id = 10, Title = "Судьба человека",          Author = "Михаил Шолохов",    Genre = "Рассказ",        Year = 1957, Description = "Рассказ о судьбе солдата, прошедшего войну и плен." }
        };

        public static BookItem Find(int id)
        {
            return Books.FirstOrDefault(b => b.Id == id);
        }
    }
}
'''

FILES['Site.Master'] = r'''
<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.Master.cs" Inherits="WebApplication3.Site" %>

<!DOCTYPE html>
<html>
<head runat="server">
    <title>Лабораторная: маршрутизация</title>
    <style>
        body { font-family: Arial; margin: 20px; }
        .menu a { margin-right: 15px; }
        .error { color: red; font-weight: bold; }
    </style>
</head>
<body>
    <form id="form1" runat="server">
        <div class="menu">
            <a href='<%: GetRouteUrl("ProductsListRoute", null) %>'>Товары</a>
            <a href='<%: GetRouteUrl("CatalogRoute", new { category = "laptops" }) %>'>Ноутбуки</a>
            <a href='<%: GetRouteUrl("StudentRoute", new { id = 1 }) %>'>Студент №1</a>
            <a href='<%: GetRouteUrl("ArchiveRoute", new { year = "2025", month = "12" }) %>'>Архив</a>
            <a href='<%: GetRouteUrl("BooksRoute", null) %>'>Книги</a>
            <a href='<%: GetRouteUrl("RouteDebugRoute", null) %>'>RouteDebug</a>
        </div>
        <hr />
        <div>
            <asp:ContentPlaceHolder ID="MainContent" runat="server" />
        </div>
    </form>
</body>
</html>
'''

FILES['Site.Master.cs'] = r'''
using System;

namespace WebApplication3
{
    public partial class Site : System.Web.UI.MasterPage
    {
        protected void Page_Load(object sender, EventArgs e) { }
    }
}
'''

FILES['Site.Master.designer.cs'] = r'''
namespace WebApplication3
{
    public partial class Site
    {
        protected global::System.Web.UI.HtmlControls.HtmlForm form1;
        protected global::System.Web.UI.WebControls.ContentPlaceHolder MainContent;
    }
}
'''

FILES['Student.aspx'] = r'''
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Student.aspx.cs" Inherits="WebApplication3.Student" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h1>Профиль студента</h1>
    <asp:PlaceHolder ID="phProfile" runat="server" Visible="false">
        <p><b>ФИО:</b> <asp:Literal ID="lblName" runat="server" /></p>
        <p><b>Группа:</b> <asp:Literal ID="lblGroup" runat="server" /></p>
        <p><b>Курс:</b> <asp:Literal ID="lblCourse" runat="server" /></p>
    </asp:PlaceHolder>
    <asp:Label ID="lblError" runat="server" CssClass="error" Visible="false" />
</asp:Content>
'''

FILES['Student.aspx.cs'] = r'''
using System;

namespace WebApplication3
{
    public partial class Student : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            object idObj = RouteData.Values["id"];

            if (idObj == null)
            {
                ShowError("Ошибка: параметр id отсутствует в адресе.");
                return;
            }

            int id;
            if (!int.TryParse(idObj.ToString(), out id) || id <= 0)
            {
                ShowError("Ошибка: id должен быть целым положительным числом.");
                return;
            }

            string name, group;
            int course;
            bool found = true;

            switch (id)
            {
                case 1:   name = "Иванов Иван Иванович";    group = "ИСП-31"; course = 3; break;
                case 25:  name = "Петров Пётр Петрович";    group = "ИСП-22"; course = 2; break;
                case 100: name = "Сидорова Анна Сергеевна"; group = "ИСП-44"; course = 4; break;
                default:  name = null; group = null; course = 0; found = false; break;
            }

            if (!found)
            {
                ShowError("Студент не найден");
                return;
            }

            lblName.Text = name;
            lblGroup.Text = group;
            lblCourse.Text = course.ToString();
            phProfile.Visible = true;
        }

        private void ShowError(string message)
        {
            phProfile.Visible = false;
            lblError.Text = message;
            lblError.Visible = true;
        }
    }
}
'''

FILES['Student.aspx.designer.cs'] = r'''
namespace WebApplication3
{
    public partial class Student
    {
        protected global::System.Web.UI.WebControls.PlaceHolder phProfile;
        protected global::System.Web.UI.WebControls.Literal lblName;
        protected global::System.Web.UI.WebControls.Literal lblGroup;
        protected global::System.Web.UI.WebControls.Literal lblCourse;
        protected global::System.Web.UI.WebControls.Label lblError;
    }
}
'''

FILES['Products.aspx'] = r'''
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Products.aspx.cs" Inherits="WebApplication3.Products" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h1>Каталог товаров</h1>
    <asp:Repeater ID="RepeaterProducts" runat="server">
        <ItemTemplate>
            <p>
                <%# Eval("Id") %>.
                <a href='<%# GetRouteUrl("ProductsRoute", new { id = Eval("Id") }) %>'><%# Eval("Name") %></a>
                — <%# Eval("Category") %>, <%# Eval("Price", "{0:N0}") %> руб.
            </p>
        </ItemTemplate>
    </asp:Repeater>
</asp:Content>
'''

FILES['Products.aspx.cs'] = r'''
using System;

namespace WebApplication3
{
    public partial class Products : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            RepeaterProducts.DataSource = Shop.Products;
            RepeaterProducts.DataBind();
        }
    }
}
'''

FILES['Products.aspx.designer.cs'] = r'''
namespace WebApplication3
{
    public partial class Products
    {
        protected global::System.Web.UI.WebControls.Repeater RepeaterProducts;
    }
}
'''

FILES['Product.aspx'] = r'''
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Product.aspx.cs" Inherits="WebApplication3.Product" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h1>Информация о товаре</h1>
    <asp:Panel ID="pnlProduct" runat="server" Visible="false">
        <h2><asp:Literal ID="lblName" runat="server" /></h2>
        <p><b>Категория:</b> <asp:Literal ID="lblCategory" runat="server" /></p>
        <p><b>Цена:</b> <asp:Literal ID="lblPrice" runat="server" /> руб.</p>
        <p><b>Описание:</b> <asp:Literal ID="lblDescription" runat="server" /></p>
    </asp:Panel>
    <asp:Label ID="lblError" runat="server" CssClass="error" Visible="false" />
</asp:Content>
'''

FILES['Product.aspx.cs'] = r'''
using System;

namespace WebApplication3
{
    public partial class Product : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            object idObj = RouteData.Values["id"];
            if (idObj == null) { ShowError("Ошибка: не передан id товара."); return; }

            int id;
            if (!int.TryParse(idObj.ToString(), out id)) { ShowError("Ошибка: id должен быть числом."); return; }

            var product = Shop.Find(id);
            if (product == null) { ShowError("Товар не найден"); return; }

            lblName.Text = product.Name;
            lblCategory.Text = product.Category;
            lblPrice.Text = product.Price.ToString("N0");
            lblDescription.Text = product.Description;
            pnlProduct.Visible = true;
        }

        private void ShowError(string message)
        {
            pnlProduct.Visible = false;
            lblError.Text = message;
            lblError.Visible = true;
        }
    }
}
'''

FILES['Product.aspx.designer.cs'] = r'''
namespace WebApplication3
{
    public partial class Product
    {
        protected global::System.Web.UI.WebControls.Panel pnlProduct;
        protected global::System.Web.UI.WebControls.Literal lblName;
        protected global::System.Web.UI.WebControls.Literal lblCategory;
        protected global::System.Web.UI.WebControls.Literal lblPrice;
        protected global::System.Web.UI.WebControls.Literal lblDescription;
        protected global::System.Web.UI.WebControls.Label lblError;
    }
}
'''

FILES['Catalog.aspx'] = r'''
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Catalog.aspx.cs" Inherits="WebApplication3.Catalog" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h1>Категория: <asp:Literal ID="lblCategory" runat="server" /></h1>
    <p>
        <a href='<%: GetRouteUrl("CatalogRoute", new { category = "laptops" }) %>'>Ноутбуки</a> |
        <a href='<%: GetRouteUrl("CatalogRoute", new { category = "phones" }) %>'>Телефоны</a> |
        <a href='<%: GetRouteUrl("CatalogRoute", new { category = "tablets" }) %>'>Планшеты</a>
    </p>

    <asp:Panel ID="pnlProduct" runat="server" Visible="false">
        <h2><asp:Literal ID="lblName" runat="server" /></h2>
        <p><b>Цена:</b> <asp:Literal ID="lblPrice" runat="server" /> руб.</p>
        <p><b>Описание:</b> <asp:Literal ID="lblDescription" runat="server" /></p>
        <asp:HyperLink ID="lnkBack" runat="server" Text="← ко всем товарам категории" />
    </asp:Panel>

    <asp:Repeater ID="RepeaterProducts" runat="server">
        <ItemTemplate>
            <p>
                <a href='<%# GetRouteUrl("CatalogItemRoute", new { category = Eval("Category"), id = Eval("Id") }) %>'><%# Eval("Name") %></a>
                — <%# Eval("Price", "{0:N0}") %> руб.
            </p>
        </ItemTemplate>
    </asp:Repeater>

    <asp:Label ID="lblError" runat="server" CssClass="error" Visible="false" />
</asp:Content>
'''

FILES['Catalog.aspx.cs'] = r'''
using System;
using System.Linq;

namespace WebApplication3
{
    public partial class Catalog : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            string category = Convert.ToString(RouteData.Values["category"]);

            string canonical = Shop.Categories
                .FirstOrDefault(c => string.Equals(c, category, StringComparison.OrdinalIgnoreCase));

            if (canonical == null)
            {
                ShowError("Категория \"" + category + "\" не найдена.");
                return;
            }

            lblCategory.Text = canonical;

            object idObj = RouteData.Values["id"];

            if (idObj == null)
            {
                RepeaterProducts.DataSource = Shop.Products.Where(p => p.Category == canonical).ToList();
                RepeaterProducts.DataBind();
                return;
            }

            int id;
            if (!int.TryParse(idObj.ToString(), out id))
            {
                ShowError("Ошибка: id должен быть числом.");
                return;
            }

            var product = Shop.Find(id);
            if (product == null) { ShowError("Товар не найден."); return; }

            if (!string.Equals(product.Category, canonical, StringComparison.OrdinalIgnoreCase))
            {
                ShowError("Некорректный адрес: товар №" + id + " не относится к категории \"" + canonical + "\".");
                return;
            }

            lblName.Text = product.Name;
            lblPrice.Text = product.Price.ToString("N0");
            lblDescription.Text = product.Description;
            lnkBack.NavigateUrl = GetRouteUrl("CatalogRoute", new { category = canonical });
            pnlProduct.Visible = true;
            RepeaterProducts.Visible = false;
        }

        private void ShowError(string message)
        {
            pnlProduct.Visible = false;
            RepeaterProducts.Visible = false;
            lblError.Text = message;
            lblError.Visible = true;
        }
    }
}
'''

FILES['Catalog.aspx.designer.cs'] = r'''
namespace WebApplication3
{
    public partial class Catalog
    {
        protected global::System.Web.UI.WebControls.Literal lblCategory;
        protected global::System.Web.UI.WebControls.Panel pnlProduct;
        protected global::System.Web.UI.WebControls.Literal lblName;
        protected global::System.Web.UI.WebControls.Literal lblPrice;
        protected global::System.Web.UI.WebControls.Literal lblDescription;
        protected global::System.Web.UI.WebControls.HyperLink lnkBack;
        protected global::System.Web.UI.WebControls.Repeater RepeaterProducts;
        protected global::System.Web.UI.WebControls.Label lblError;
    }
}
'''

FILES['Archive.aspx'] = r'''
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Archive.aspx.cs" Inherits="WebApplication3.Archive" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h1>Архив материалов</h1>
    <p>Выбранный период: <b><asp:Literal ID="lblPeriod" runat="server" /></b></p>
    <asp:Repeater ID="RepeaterMaterials" runat="server">
        <ItemTemplate><p><%# Container.DataItem %></p></ItemTemplate>
    </asp:Repeater>
</asp:Content>
'''

FILES['Archive.aspx.cs'] = r'''
using System;
using System.Collections.Generic;

namespace WebApplication3
{
    public partial class Archive : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            string year = RouteData.Values["year"].ToString();
            string month = RouteData.Values["month"].ToString();

            string[] monthsRu =
            {
                "", "январь", "февраль", "март", "апрель", "май", "июнь",
                "июль", "август", "сентябрь", "октябрь", "ноябрь", "декабрь"
            };
            int monthNum = int.Parse(month);

            lblPeriod.Text = monthNum + " " + year + " (" + month + "/" + year + ")";

            var materials = new List<string>();
            for (int i = 1; i <= 3; i++)
                materials.Add("Материал №" + i + " — обзор за " + monthsRu[monthNum] + " " + year + " года");

            RepeaterMaterials.DataSource = materials;
            RepeaterMaterials.DataBind();
        }
    }
}
'''

FILES['Archive.aspx.designer.cs'] = r'''
namespace WebApplication3
{
    public partial class Archive
    {
        protected global::System.Web.UI.WebControls.Literal lblPeriod;
        protected global::System.Web.UI.WebControls.Repeater RepeaterMaterials;
    }
}
'''

FILES['NewProduct.aspx'] = r'''
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="NewProduct.aspx.cs" Inherits="WebApplication3.NewProduct" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h1>Создание нового товара</h1>
    <p>Отдельная страница для адреса <b>/products/new</b>.</p>
    <p>Здесь могла бы быть форма создания товара.</p>
</asp:Content>
'''

FILES['NewProduct.aspx.cs'] = r'''
using System;

namespace WebApplication3
{
    public partial class NewProduct : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e) { }
    }
}
'''

FILES['NewProduct.aspx.designer.cs'] = r'''
namespace WebApplication3
{
    public partial class NewProduct
    {
    }
}
'''

FILES['Books.aspx'] = r'''
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Books.aspx.cs" Inherits="WebApplication3.Books" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h1>Библиотека — список книг</h1>
    <p><asp:Literal ID="lblPage" runat="server" /></p>
    <asp:Repeater ID="RepeaterBooks" runat="server">
        <ItemTemplate>
            <p>
                <a href='<%# GetRouteUrl("BookRoute", new { id = Eval("Id") }) %>'><b><%# Eval("Title") %></b></a><br />
                Автор:
                <a href='<%# GetRouteUrl("AuthorRoute", new { name = Eval("Author") }) %>'><%# Eval("Author") %></a>
                | Жанр:
                <a href='<%# GetRouteUrl("GenreRoute", new { name = Eval("Genre") }) %>'><%# Eval("Genre") %></a>
                | Год: <%# Eval("Year") %>
            </p>
        </ItemTemplate>
    </asp:Repeater>
</asp:Content>
'''

FILES['Books.aspx.cs'] = r'''
using System;

namespace WebApplication3
{
    public partial class Books : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            int page = 1;
            if (RouteData.Values["page"] != null)
                int.TryParse(RouteData.Values["page"].ToString(), out page);

            lblPage.Text = "Страница: " + page;

            RepeaterBooks.DataSource = Library.Books;
            RepeaterBooks.DataBind();
        }
    }
}
'''

FILES['Books.aspx.designer.cs'] = r'''
namespace WebApplication3
{
    public partial class Books
    {
        protected global::System.Web.UI.WebControls.Literal lblPage;
        protected global::System.Web.UI.WebControls.Repeater RepeaterBooks;
    }
}
'''

FILES['Book.aspx'] = r'''
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Book.aspx.cs" Inherits="WebApplication3.Book" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h1>Книга</h1>
    <asp:Panel ID="pnlBook" runat="server" Visible="false">
        <h2><asp:Literal ID="lblTitle" runat="server" /></h2>
        <p><b>Автор:</b> <a id="lnkAuthor" runat="server"></a></p>
        <p><b>Жанр:</b> <a id="lnkGenre" runat="server"></a></p>
        <p><b>Год:</b> <asp:Literal ID="lblYear" runat="server" /></p>
        <p><b>Описание:</b> <asp:Literal ID="lblDescription" runat="server" /></p>
    </asp:Panel>
    <asp:Label ID="lblError" runat="server" CssClass="error" Visible="false" />
</asp:Content>
'''

FILES['Book.aspx.cs'] = r'''
using System;

namespace WebApplication3
{
    public partial class Book : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            object idObj = RouteData.Values["id"];
            if (idObj == null) { ShowError("Ошибка: не передан id книги."); return; }

            int id;
            if (!int.TryParse(idObj.ToString(), out id)) { ShowError("Ошибка: id должен быть числом."); return; }

            var book = Library.Find(id);
            if (book == null) { ShowError("Книга не найдена"); return; }

            object nameObj = RouteData.Values["name"];
            if (nameObj != null)
            {
                string name = nameObj.ToString();
                if (!string.Equals(book.Author, name, StringComparison.OrdinalIgnoreCase))
                {
                    ShowError("Некорректный адрес: книга \"" + book.Title + "\" написана " +
                              book.Author + ", а не " + name + ".");
                    return;
                }
            }

            lblTitle.Text = book.Title;
            lblYear.Text = book.Year.ToString();
            lblDescription.Text = book.Description;

            lnkAuthor.InnerText = book.Author;
            lnkAuthor.HRef = GetRouteUrl("AuthorRoute", new { name = book.Author });

            lnkGenre.InnerText = book.Genre;
            lnkGenre.HRef = GetRouteUrl("GenreRoute", new { name = book.Genre });

            pnlBook.Visible = true;
        }

        private void ShowError(string message)
        {
            pnlBook.Visible = false;
            lblError.Text = message;
            lblError.Visible = true;
        }
    }
}
'''

FILES['Book.aspx.designer.cs'] = r'''
namespace WebApplication3
{
    public partial class Book
    {
        protected global::System.Web.UI.WebControls.Panel pnlBook;
        protected global::System.Web.UI.WebControls.Literal lblTitle;
        protected global::System.Web.UI.HtmlControls.HtmlAnchor lnkAuthor;
        protected global::System.Web.UI.HtmlControls.HtmlAnchor lnkGenre;
        protected global::System.Web.UI.WebControls.Literal lblYear;
        protected global::System.Web.UI.WebControls.Literal lblDescription;
        protected global::System.Web.UI.WebControls.Label lblError;
    }
}
'''

FILES['Author.aspx'] = r'''
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Author.aspx.cs" Inherits="WebApplication3.Author" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h1>Автор: <asp:Literal ID="lblAuthor" runat="server" /></h1>
    <p>Книги этого автора:</p>
    <asp:Repeater ID="RepeaterBooks" runat="server">
        <ItemTemplate>
            <p>
                <a href='<%# GetRouteUrl("BookRoute", new { id = Eval("Id") }) %>'><%# Eval("Title") %></a>
                (<%# Eval("Year") %>, <%# Eval("Genre") %>)
            </p>
        </ItemTemplate>
    </asp:Repeater>
    <asp:Label ID="lblError" runat="server" CssClass="error" Visible="false" />
</asp:Content>
'''

FILES['Author.aspx.cs'] = r'''
using System;
using System.Linq;

namespace WebApplication3
{
    public partial class Author : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            string name = Convert.ToString(RouteData.Values["name"]);

            var books = Library.Books
                .Where(b => string.Equals(b.Author, name, StringComparison.OrdinalIgnoreCase))
                .ToList();

            if (books.Count == 0) { ShowError("Автор не найден"); return; }

            lblAuthor.Text = books[0].Author;
            RepeaterBooks.DataSource = books;
            RepeaterBooks.DataBind();
        }

        private void ShowError(string message)
        {
            lblAuthor.Text = "?";
            RepeaterBooks.Visible = false;
            lblError.Text = message;
            lblError.Visible = true;
        }
    }
}
'''

FILES['Author.aspx.designer.cs'] = r'''
namespace WebApplication3
{
    public partial class Author
    {
        protected global::System.Web.UI.WebControls.Literal lblAuthor;
        protected global::System.Web.UI.WebControls.Repeater RepeaterBooks;
        protected global::System.Web.UI.WebControls.Label lblError;
    }
}
'''

FILES['Genre.aspx'] = r'''
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Genre.aspx.cs" Inherits="WebApplication3.Genre" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h1>Жанр: <asp:Literal ID="lblGenre" runat="server" /></h1>
    <p>Книги этого жанра:</p>
    <asp:Repeater ID="RepeaterBooks" runat="server">
        <ItemTemplate>
            <p>
                <a href='<%# GetRouteUrl("BookRoute", new { id = Eval("Id") }) %>'><%# Eval("Title") %></a>
                (<%# Eval("Author") %>, <%# Eval("Year") %>)
            </p>
        </ItemTemplate>
    </asp:Repeater>
    <asp:Label ID="lblError" runat="server" CssClass="error" Visible="false" />
</asp:Content>
'''

FILES['Genre.aspx.cs'] = r'''
using System;
using System.Linq;

namespace WebApplication3
{
    public partial class Genre : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            string name = Convert.ToString(RouteData.Values["name"]);

            var books = Library.Books
                .Where(b => string.Equals(b.Genre, name, StringComparison.OrdinalIgnoreCase))
                .ToList();

            if (books.Count == 0) { ShowError("Жанр не найден"); return; }

            lblGenre.Text = books[0].Genre;
            RepeaterBooks.DataSource = books;
            RepeaterBooks.DataBind();
        }

        private void ShowError(string message)
        {
            lblGenre.Text = "?";
            RepeaterBooks.Visible = false;
            lblError.Text = message;
            lblError.Visible = true;
        }
    }
}
'''

FILES['Genre.aspx.designer.cs'] = r'''
namespace WebApplication3
{
    public partial class Genre
    {
        protected global::System.Web.UI.WebControls.Literal lblGenre;
        protected global::System.Web.UI.WebControls.Repeater RepeaterBooks;
        protected global::System.Web.UI.WebControls.Label lblError;
    }
}
'''

FILES['Search.aspx'] = r'''
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Search.aspx.cs" Inherits="WebApplication3.Search" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h1>Поиск книг</h1>
    <p>Результаты по запросу «<asp:Literal ID="lblQuery" runat="server" />»:</p>
    <asp:Repeater ID="RepeaterBooks" runat="server">
        <ItemTemplate>
            <p>
                <a href='<%# GetRouteUrl("BookRoute", new { id = Eval("Id") }) %>'><%# Eval("Title") %></a>
                — <%# Eval("Author") %>, <%# Eval("Year") %>
            </p>
        </ItemTemplate>
    </asp:Repeater>
    <asp:Label ID="lblError" runat="server" CssClass="error" Visible="false" />
</asp:Content>
'''

FILES['Search.aspx.cs'] = r'''
using System;
using System.Linq;

namespace WebApplication3
{
    public partial class Search : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            string text = Convert.ToString(RouteData.Values["text"]);
            lblQuery.Text = text;

            var results = Library.Books
                .Where(b => b.Title.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0)
                .ToList();

            if (results.Count == 0)
            {
                RepeaterBooks.Visible = false;
                lblError.Text = "По запросу \"" + text + "\" ничего не найдено";
                lblError.Visible = true;
                return;
            }

            RepeaterBooks.DataSource = results;
            RepeaterBooks.DataBind();
        }
    }
}
'''

FILES['Search.aspx.designer.cs'] = r'''
namespace WebApplication3
{
    public partial class Search
    {
        protected global::System.Web.UI.WebControls.Literal lblQuery;
        protected global::System.Web.UI.WebControls.Repeater RepeaterBooks;
        protected global::System.Web.UI.WebControls.Label lblError;
    }
}
'''

FILES['RouteDebug.aspx'] = r'''
<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="RouteDebug.aspx.cs" Inherits="WebApplication3.RouteDebug" %>
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h1>RouteDebug</h1>
    <h2>Все значения RouteData.Values текущего запроса</h2>
    <asp:Literal ID="lblValues" runat="server" />
</asp:Content>
'''

FILES['RouteDebug.aspx.cs'] = r'''
using System;
using System.Text;

namespace WebApplication3
{
    public partial class RouteDebug : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            var sb = new StringBuilder();
            sb.Append("<table border='1' cellpadding='4'>");
            foreach (string key in RouteData.Values.Keys)
            {
                sb.Append("<tr><td><b>").Append(Server.HtmlEncode(key))
                  .Append("</b></td><td>")
                  .Append(Server.HtmlEncode(Convert.ToString(RouteData.Values[key])))
                  .Append("</td></tr>");
            }
            sb.Append("</table>");
            lblValues.Text = sb.ToString();
        }
    }
}
'''

FILES['RouteDebug.aspx.designer.cs'] = r'''
namespace WebApplication3
{
    public partial class RouteDebug
    {
        protected global::System.Web.UI.WebControls.Literal lblValues;
    }
}
'''

for name, content in FILES.items():
    path = os.path.join(root, name)
    text = content.strip().replace('\r\n', '\n')
    with open(path, 'w', encoding='utf-8-sig', newline='\r\n') as f:
        f.write(text)
    print('создан:', name)

projects = glob.glob(os.path.join(root, '*.csproj'))
if not projects:
    print('ВНИМАНИЕ: csproj не найден. Положи скрипт в папку проекта (рядом с .csproj).')
else:
    csproj = projects[0]
    with open(csproj, 'r', encoding='utf-8-sig') as f:
        text = f.read().replace('\r\n', '\n')

    entries = []

    def add(tag, include, dep=None, subtype=None):
        entries.append((tag, include, dep, subtype))

    add('Content', 'Global.asax')
    add('Compile', 'Global.asax.cs', 'Global.asax')
    add('Content', 'Site.Master')
    add('Compile', 'Site.Master.cs', 'Site.Master', 'ASPXCodeBehind')
    add('Compile', 'Site.Master.designer.cs', 'Site.Master')
    add('Compile', 'Data.cs')
    add('Compile', 'RouteConfig.cs')
    for p in ['Student', 'Products', 'Product', 'Catalog', 'Archive', 'NewProduct',
              'Books', 'Book', 'Author', 'Genre', 'Search', 'RouteDebug']:
        add('Content', p + '.aspx')
        add('Compile', p + '.aspx.cs', p + '.aspx', 'ASPXCodeBehind')
        add('Compile', p + '.aspx.designer.cs', p + '.aspx')

    lines = ['  <ItemGroup>']
    added = 0
    for tag, include, dep, subtype in entries:
        if ('Include="%s"' % include) in text:
            continue
        added += 1
        if dep is None and subtype is None:
            lines.append('    <%s Include="%s" />' % (tag, include))
        else:
            lines.append('    <%s Include="%s">' % (tag, include))
            if dep:
                lines.append('      <DependentUpon>%s</DependentUpon>' % dep)
            if subtype:
                lines.append('      <SubType>%s</SubType>' % subtype)
            lines.append('    </%s>' % tag)
    lines.append('  </ItemGroup>')
    block = '\n'.join(lines) + '\n'

    if added > 0:
        idx = text.rfind('</Project>')
        text = text[:idx] + block + '\n' + text[idx:]
        with open(csproj, 'w', encoding='utf-8-sig', newline='\r\n') as f:
            f.write(text)
        print('csproj обновлён, добавлено записей:', added)
    else:
        print('csproj уже содержит все файлы.')

print()
print('Готово. Открывай Visual Studio и делай Rebuild Solution.')