StudentForm.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. using CsvHelper;
  2. using Guna.UI2.WinForms;
  3. using Npgsql;
  4. using System;
  5. using System.Data;
  6. using System.Globalization;
  7. using System.IO;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. using System.Windows.Forms;
  11. using System.Linq;
  12. namespace DeanOfficeApp
  13. {
  14. public partial class StudentForm : Form
  15. {
  16. private Guna2DataGridView dgvStudents;
  17. private Guna2Button btnImportCsv;
  18. private Guna2ComboBox cmbGroupFilter;
  19. private Guna2Button btnRefresh;
  20. private Guna2HtmlLabel lblFilter;
  21. private Guna2TextBox txtSearch;
  22. private DataTable _studentsTable;
  23. private readonly string _connString = "Host=245e1-rw.db.pub.dbaas.postgrespro.ru;Port=5432;Database=dbpractice;Username=mardasov_mv;Password=A4%2x$$2Ynr";
  24. public StudentForm()
  25. {
  26. InitializeComponent();
  27. this.Load += async (s, e) => await LoadAllDataAsync();
  28. }
  29. private async Task LoadAllDataAsync()
  30. {
  31. await LoadGroupsAsync();
  32. await LoadStudentsAsync();
  33. }
  34. private async Task LoadGroupsAsync()
  35. {
  36. try
  37. {
  38. using (var conn = new NpgsqlConnection(_connString))
  39. {
  40. await conn.OpenAsync();
  41. var cmd = new NpgsqlCommand("SELECT group_id, name FROM \"Group\" ORDER BY name", conn);
  42. var dt = new DataTable();
  43. dt.Load(await cmd.ExecuteReaderAsync());
  44. cmbGroupFilter.DisplayMember = "name";
  45. cmbGroupFilter.ValueMember = "group_id";
  46. cmbGroupFilter.DataSource = dt;
  47. cmbGroupFilter.SelectedIndex = -1;
  48. }
  49. }
  50. catch (Exception ex)
  51. {
  52. MessageBox.Show($"Ошибка загрузки групп: {ex.Message}");
  53. }
  54. }
  55. private async Task LoadStudentsAsync(int? groupId = null)
  56. {
  57. string sql = @"
  58. SELECT
  59. s.record_book,
  60. s.full_name,
  61. g.name AS group_name,
  62. COALESCE(AVG(p.grade), 0) AS average_grade
  63. FROM Student s
  64. LEFT JOIN ""Group"" g ON s.group_id = g.group_id
  65. LEFT JOIN Progress p ON s.record_book = p.record_book
  66. ";
  67. if (groupId.HasValue)
  68. sql += " WHERE g.group_id = @gid";
  69. sql += " GROUP BY s.record_book, s.full_name, g.name ORDER BY s.full_name";
  70. try
  71. {
  72. Cursor = Cursors.WaitCursor;
  73. using (var conn = new NpgsqlConnection(_connString))
  74. {
  75. await conn.OpenAsync();
  76. using (var cmd = new NpgsqlCommand(sql, conn))
  77. {
  78. if (groupId.HasValue)
  79. cmd.Parameters.AddWithValue("@gid", groupId.Value);
  80. var dt = new DataTable();
  81. dt.Load(await cmd.ExecuteReaderAsync());
  82. _studentsTable = dt;
  83. ApplyFilter();
  84. }
  85. }
  86. }
  87. catch (Exception ex)
  88. {
  89. MessageBox.Show($"Ошибка загрузки студентов: {ex.Message}");
  90. }
  91. finally
  92. {
  93. Cursor = Cursors.Default;
  94. }
  95. }
  96. private void ApplyFilter()
  97. {
  98. if (_studentsTable == null) return;
  99. string filterText = txtSearch.Text.Trim().ToLower();
  100. if (string.IsNullOrEmpty(filterText))
  101. {
  102. dgvStudents.DataSource = _studentsTable;
  103. }
  104. else
  105. {
  106. var filteredRows = _studentsTable.AsEnumerable()
  107. .Where(row => row["full_name"].ToString().ToLower().Contains(filterText))
  108. .CopyToDataTable();
  109. dgvStudents.DataSource = filteredRows;
  110. }
  111. if (dgvStudents.Columns.Contains("record_book"))
  112. dgvStudents.Columns["record_book"].Visible = false;
  113. dgvStudents.Columns["average_grade"].HeaderText = "Средний балл";
  114. dgvStudents.Columns["average_grade"].DefaultCellStyle.Format = "0.00";
  115. }
  116. private async void btnRefresh_Click(object sender, EventArgs e)
  117. {
  118. int? groupId = cmbGroupFilter.SelectedValue as int?;
  119. await LoadStudentsAsync(groupId);
  120. }
  121. private async void cmbGroupFilter_SelectedIndexChanged(object sender, EventArgs e)
  122. {
  123. int? groupId = cmbGroupFilter.SelectedValue as int?;
  124. await LoadStudentsAsync(groupId);
  125. }
  126. private void txtSearch_TextChanged(object sender, EventArgs e)
  127. {
  128. ApplyFilter();
  129. }
  130. private async void btnImportCsv_Click(object sender, EventArgs e)
  131. {
  132. using (OpenFileDialog ofd = new OpenFileDialog())
  133. {
  134. ofd.Filter = "CSV files (*.csv)|*.csv";
  135. if (ofd.ShowDialog() == DialogResult.OK)
  136. {
  137. try
  138. {
  139. Cursor = Cursors.WaitCursor;
  140. using (var reader = new StreamReader(ofd.FileName, Encoding.UTF8))
  141. using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
  142. {
  143. var records = csv.GetRecords<dynamic>();
  144. int inserted = 0, skipped = 0;
  145. foreach (var rec in records)
  146. {
  147. string fullName = rec.full_name;
  148. string birthDateStr = rec.birth_date;
  149. string address = rec.address;
  150. int groupId = int.Parse(rec.group_id);
  151. using (var conn = new NpgsqlConnection(_connString))
  152. {
  153. await conn.OpenAsync();
  154. var check = new NpgsqlCommand("SELECT COUNT(*) FROM Student WHERE full_name = @name AND birth_date = @birth", conn);
  155. check.Parameters.AddWithValue("@name", fullName);
  156. check.Parameters.AddWithValue("@birth", DateTime.Parse(birthDateStr));
  157. long cnt = (long)await check.ExecuteScalarAsync();
  158. if (cnt == 0)
  159. {
  160. var ins = new NpgsqlCommand("INSERT INTO Student (full_name, birth_date, address, group_id) VALUES (@f, @b, @a, @g)", conn);
  161. ins.Parameters.AddWithValue("@f", fullName);
  162. ins.Parameters.AddWithValue("@b", DateTime.Parse(birthDateStr));
  163. ins.Parameters.AddWithValue("@a", address);
  164. ins.Parameters.AddWithValue("@g", groupId);
  165. await ins.ExecuteNonQueryAsync();
  166. inserted++;
  167. }
  168. else skipped++;
  169. }
  170. }
  171. MessageBox.Show($"Импорт завершён.\nДобавлено: {inserted}\nПропущено (дубликаты): {skipped}");
  172. await LoadStudentsAsync(cmbGroupFilter.SelectedValue as int?);
  173. }
  174. }
  175. catch (Exception ex)
  176. {
  177. MessageBox.Show("Ошибка импорта: " + ex.Message);
  178. }
  179. finally
  180. {
  181. Cursor = Cursors.Default;
  182. }
  183. }
  184. }
  185. }
  186. // Дизайнер
  187. private void InitializeComponent()
  188. {
  189. System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
  190. System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle();
  191. System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle();
  192. this.dgvStudents = new Guna.UI2.WinForms.Guna2DataGridView();
  193. this.btnImportCsv = new Guna.UI2.WinForms.Guna2Button();
  194. this.cmbGroupFilter = new Guna.UI2.WinForms.Guna2ComboBox();
  195. this.btnRefresh = new Guna.UI2.WinForms.Guna2Button();
  196. this.lblFilter = new Guna.UI2.WinForms.Guna2HtmlLabel();
  197. this.txtSearch = new Guna.UI2.WinForms.Guna2TextBox();
  198. ((System.ComponentModel.ISupportInitialize)(this.dgvStudents)).BeginInit();
  199. this.SuspendLayout();
  200. //
  201. // dgvStudents
  202. //
  203. this.dgvStudents.AllowUserToAddRows = false;
  204. this.dgvStudents.AllowUserToDeleteRows = false;
  205. dataGridViewCellStyle1.BackColor = System.Drawing.Color.White;
  206. this.dgvStudents.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle1;
  207. dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
  208. dataGridViewCellStyle2.BackColor = System.Drawing.Color.FromArgb(100, 88, 255);
  209. dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
  210. dataGridViewCellStyle2.ForeColor = System.Drawing.Color.White;
  211. dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight;
  212. dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
  213. dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
  214. this.dgvStudents.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle2;
  215. this.dgvStudents.ColumnHeadersHeight = 40;
  216. dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
  217. dataGridViewCellStyle3.BackColor = System.Drawing.Color.White;
  218. dataGridViewCellStyle3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204)));
  219. dataGridViewCellStyle3.ForeColor = System.Drawing.Color.FromArgb(71, 69, 94);
  220. dataGridViewCellStyle3.SelectionBackColor = System.Drawing.Color.FromArgb(231, 229, 255);
  221. dataGridViewCellStyle3.SelectionForeColor = System.Drawing.Color.FromArgb(71, 69, 94);
  222. dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.False;
  223. this.dgvStudents.DefaultCellStyle = dataGridViewCellStyle3;
  224. this.dgvStudents.GridColor = System.Drawing.Color.FromArgb(230, 230, 230);
  225. this.dgvStudents.Location = new System.Drawing.Point(20, 60);
  226. this.dgvStudents.Name = "dgvStudents";
  227. this.dgvStudents.ReadOnly = true;
  228. this.dgvStudents.RowHeadersVisible = false;
  229. this.dgvStudents.RowTemplate.Height = 30;
  230. this.dgvStudents.Size = new System.Drawing.Size(760, 420);
  231. this.dgvStudents.TabIndex = 0;
  232. //
  233. // btnImportCsv
  234. //
  235. this.btnImportCsv.BorderRadius = 10;
  236. this.btnImportCsv.FillColor = System.Drawing.Color.FromArgb(40, 167, 69);
  237. this.btnImportCsv.Font = new System.Drawing.Font("Segoe UI", 9F);
  238. this.btnImportCsv.ForeColor = System.Drawing.Color.White;
  239. this.btnImportCsv.Location = new System.Drawing.Point(620, 12);
  240. this.btnImportCsv.Name = "btnImportCsv";
  241. this.btnImportCsv.Size = new System.Drawing.Size(160, 36);
  242. this.btnImportCsv.TabIndex = 1;
  243. this.btnImportCsv.Text = "📂 Импорт студентов (CSV)";
  244. this.btnImportCsv.Click += new System.EventHandler(this.btnImportCsv_Click);
  245. //
  246. // cmbGroupFilter
  247. //
  248. this.cmbGroupFilter.BackColor = System.Drawing.Color.Transparent;
  249. this.cmbGroupFilter.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
  250. this.cmbGroupFilter.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
  251. this.cmbGroupFilter.FocusedColor = System.Drawing.Color.Empty;
  252. this.cmbGroupFilter.Font = new System.Drawing.Font("Segoe UI", 9F);
  253. this.cmbGroupFilter.ForeColor = System.Drawing.Color.FromArgb(68, 88, 112);
  254. this.cmbGroupFilter.ItemHeight = 30;
  255. this.cmbGroupFilter.Location = new System.Drawing.Point(120, 12);
  256. this.cmbGroupFilter.Name = "cmbGroupFilter";
  257. this.cmbGroupFilter.Size = new System.Drawing.Size(180, 36);
  258. this.cmbGroupFilter.TabIndex = 2;
  259. this.cmbGroupFilter.SelectedIndexChanged += new System.EventHandler(this.cmbGroupFilter_SelectedIndexChanged);
  260. //
  261. // btnRefresh
  262. //
  263. this.btnRefresh.BorderRadius = 10;
  264. this.btnRefresh.FillColor = System.Drawing.Color.FromArgb(52, 73, 94);
  265. this.btnRefresh.Font = new System.Drawing.Font("Segoe UI", 9F);
  266. this.btnRefresh.ForeColor = System.Drawing.Color.White;
  267. this.btnRefresh.Location = new System.Drawing.Point(320, 12);
  268. this.btnRefresh.Name = "btnRefresh";
  269. this.btnRefresh.Size = new System.Drawing.Size(100, 36);
  270. this.btnRefresh.TabIndex = 3;
  271. this.btnRefresh.Text = "Обновить";
  272. this.btnRefresh.Click += new System.EventHandler(this.btnRefresh_Click);
  273. //
  274. // lblFilter
  275. //
  276. this.lblFilter.BackColor = System.Drawing.Color.Transparent;
  277. this.lblFilter.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold);
  278. this.lblFilter.Location = new System.Drawing.Point(20, 22);
  279. this.lblFilter.Name = "lblFilter";
  280. this.lblFilter.Size = new System.Drawing.Size(109, 17);
  281. this.lblFilter.TabIndex = 4;
  282. this.lblFilter.Text = "Фильтр по группе:";
  283. //
  284. // txtSearch
  285. //
  286. this.txtSearch.Cursor = System.Windows.Forms.Cursors.IBeam;
  287. this.txtSearch.DefaultText = "";
  288. this.txtSearch.Font = new System.Drawing.Font("Segoe UI", 9F);
  289. this.txtSearch.Location = new System.Drawing.Point(440, 12);
  290. this.txtSearch.Name = "txtSearch";
  291. this.txtSearch.PlaceholderText = "🔍 Поиск по фамилии...";
  292. this.txtSearch.Size = new System.Drawing.Size(150, 36);
  293. this.txtSearch.TabIndex = 5;
  294. this.txtSearch.TextChanged += new System.EventHandler(this.txtSearch_TextChanged);
  295. //
  296. // StudentForm
  297. //
  298. this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
  299. this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
  300. this.BackColor = System.Drawing.Color.FromArgb(240, 242, 245);
  301. this.ClientSize = new System.Drawing.Size(800, 500);
  302. this.Controls.Add(this.txtSearch);
  303. this.Controls.Add(this.lblFilter);
  304. this.Controls.Add(this.btnRefresh);
  305. this.Controls.Add(this.cmbGroupFilter);
  306. this.Controls.Add(this.btnImportCsv);
  307. this.Controls.Add(this.dgvStudents);
  308. this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
  309. this.MaximizeBox = false;
  310. this.Name = "StudentForm";
  311. this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
  312. this.Text = "Управление студентами и средний балл";
  313. ((System.ComponentModel.ISupportInitialize)(this.dgvStudents)).EndInit();
  314. this.ResumeLayout(false);
  315. this.PerformLayout();
  316. }
  317. }
  318. }