DocUA commited on
Commit
18df3b2
·
1 Parent(s): 9fe40c6

Виправлення

Browse files
Files changed (9) hide show
  1. Dockerfile +4 -16
  2. README.md +12 -42
  3. app.py +61 -253
  4. import_success.html +53 -0
  5. start.sh +2 -41
  6. templates/download.html +64 -0
  7. templates/error.html +38 -8
  8. templates/index.html +32 -91
  9. templates/result.html +17 -2
Dockerfile CHANGED
@@ -12,30 +12,18 @@ RUN mkdir -p /docker-entrypoint-initdb.d
12
  # Копіювання SQL файлу ініціалізації
13
  COPY init.sql /docker-entrypoint-initdb.d/
14
 
15
- # Встановлення Git та інших необхідних інструментів
16
  RUN rm -f /etc/apt/sources.list.d/mysql.list && \
17
  apt-get update && \
18
- apt-get install -y git python3 python3-pip wget && \
19
  pip3 install --no-cache-dir flask mysql-connector-python && \
 
20
  rm -rf /var/lib/apt/lists/*
21
 
22
- # Завантаження та розпакування бази даних Employees
23
- RUN mkdir -p /tmp/employees && \
24
- cd /tmp/employees && \
25
- git clone https://github.com/datacharmer/test_db.git && \
26
- cd test_db && \
27
- # Копіюємо скрипти імпорту в директорію ініціалізації
28
- cp employees.sql /docker-entrypoint-initdb.d/01-employees.sql && \
29
- # Зберігаємо тестові дані для пізнішого використання
30
- mkdir -p /var/lib/mysql-files && \
31
- cp -r *.dump /var/lib/mysql-files/ && \
32
- cd / && \
33
- rm -rf /tmp/employees
34
-
35
  # Копіювання файлів веб-додатку
 
36
  COPY templates /templates
37
  COPY static /static
38
- COPY app.py /app.py
39
 
40
  # Скрипт для запуску
41
  COPY start.sh /start.sh
 
12
  # Копіювання SQL файлу ініціалізації
13
  COPY init.sql /docker-entrypoint-initdb.d/
14
 
15
+ # Встановлення необхідних пакетів
16
  RUN rm -f /etc/apt/sources.list.d/mysql.list && \
17
  apt-get update && \
18
+ apt-get install -y wget python3 python3-pip && \
19
  pip3 install --no-cache-dir flask mysql-connector-python && \
20
+ apt-get clean && \
21
  rm -rf /var/lib/apt/lists/*
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  # Копіювання файлів веб-додатку
24
+ COPY app.py /app.py
25
  COPY templates /templates
26
  COPY static /static
 
27
 
28
  # Скрипт для запуску
29
  COPY start.sh /start.sh
README.md CHANGED
@@ -8,54 +8,24 @@ sdk_version: "latest"
8
  app_file: app.py
9
  pinned: false
10
  ---
11
- # MySQL Сервер 8.0.22 для навчальних цілей
12
 
13
- Цей проект розгортає MySQL Server 8.0.22 в Docker контейнері на Hugging Face Spaces для виконання практичних занять із дисципліни ІТУД.
14
 
15
- ## Інформація про проект
16
 
17
- - **Версія MySQL**: 8.0.22 (відповідно до завдання)
18
- - **Ім'я бази даних**: test_db та employees (автоматично завантажується)
19
- - **Користувач**: test_user (пароль: test_password)
20
- - **Root пароль**: root_password
21
- - **Порт**: 3306
22
 
23
- ## Корисні посилання
 
24
 
25
- ### Програмне забезпечення:
26
- - [Workbench 8.0.22 MAC](https://downloads.mysql.com/archives/workbench)
27
- - [MySQL Server 8.0.22 MAC](https://downloads.mysql.com/archives/community)
28
- - [Workbench 8.0.22 Windows](https://downloads.mysql.com/archives/workbench/)
29
- - [MySQL Server 8.0.22 Windows](https://downloads.mysql.com/archives/installer)
30
 
31
- ### Додаткові матеріали:
32
- - [Репозиторій з базою даних "Employees"](https://github.com/datacharmer/test_db)
33
- - [Відео-інструкція з вирішення помилок встановлення](https://youtu.be/9fnTrs7sKy0)
34
 
35
- ## Підключення до MySQL сервера
36
 
37
- Для підключення використовуйте MySQL Workbench 8.0.22:
38
-
39
- 1. Завантажте та встановіть MySQL Workbench з посилань вище
40
- 2. Створіть нове з'єднання з наступними параметрами:
41
- - Connection Name: HF_MySQL_Learning
42
- - Connection Method: Standard (TCP/IP)
43
- - Hostname: URL вашого Hugging Face Space
44
- - Port: 3306
45
- - Username: test_user
46
- - Password: test_password (зберегти у сховищі)
47
-
48
- ## Завдання 1.3 - Базові практики роботи
49
-
50
- Після підключення до бази даних виконайте:
51
-
52
- ```sql
53
- -- Створення таблиці (нереформатований скрипт)
54
- create table if not exists test (numbers int, words varchar (10));
55
-
56
- -- Форматований скрипт з відступами та коментарями
57
- CREATE TABLE IF NOT EXISTS test (
58
- numbers INT, -- Поле для цілих чисел
59
- words VARCHAR(10) -- Поле для текстових значень максимум 10 символів
60
- );
61
  ```
 
 
8
  app_file: app.py
9
  pinned: false
10
  ---
 
11
 
12
+ # MySQL 8.0.22 для навчальних цілей
13
 
14
+ Цей проект розгортає MySQL Server 8.0.22 для виконання практичних завдань з баз даних.
15
 
16
+ ## Навчальні завдання
 
 
 
 
17
 
18
+ ### Завдання 1.1
19
+ Встановлення та налаштування MySQL 8.0.22 - виконано автоматично.
20
 
21
+ ### Завдання 1.2
22
+ Завантаження навчальної бази даних "Employees" - доступно через веб-інтерфейс.
 
 
 
23
 
24
+ ### Завдання 1.3
25
+ Виконання базових SQL-запитів та форматування коду - доступно через веб-консоль.
 
26
 
27
+ ## Інформація про API
28
 
29
+ Для виконання SQL запитів через API використовуйте:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  ```
31
+ curl -X POST https://mysql-itud-docsa
app.py CHANGED
@@ -1,308 +1,116 @@
1
  from flask import Flask, request, jsonify, render_template
2
  import mysql.connector
3
- import json
4
  import os
5
  import time
6
 
7
- app = Flask(__name__, template_folder='templates', static_folder='static')
8
 
9
- def get_db_connection(database='test_db'):
10
- # Спробуємо підключитися кілька разів (може знадобитися, якщо MySQL ще запускається)
11
- for i in range(5):
12
- try:
13
- return mysql.connector.connect(
14
- host="localhost",
15
- user="test_user",
16
- password="test_password",
17
- database=database
18
- )
19
- except Exception as e:
20
- if i < 4: # Якщо це не остання спроба
21
- time.sleep(2) # Чекаємо 2 секунди
22
- else:
23
- raise e # Перекидаємо помилку далі
24
-
25
- def get_mysql_status():
26
  try:
27
- conn = get_db_connection()
28
- cursor = conn.cursor()
29
- cursor.execute("SELECT VERSION()")
30
- version = cursor.fetchone()[0]
31
- cursor.close()
32
- conn.close()
33
- return {"status": "online", "version": version}
34
  except Exception as e:
35
- return {"status": "offline", "error": str(e)}
 
36
 
37
- def check_employees_db():
 
 
 
 
38
  try:
39
- # Перевіряємо чи існує база даних employees
40
  conn = get_db_connection()
41
  cursor = conn.cursor()
42
- cursor.execute("SHOW DATABASES LIKE 'employees'")
43
- exists = cursor.fetchone() is not None
44
- cursor.close()
45
- conn.close()
46
 
47
- if exists:
48
- # Якщо база існує, перевіряємо наявність таблиць
49
- conn = get_db_connection('employees')
50
- cursor = conn.cursor()
51
- cursor.execute("SHOW TABLES")
52
- tables = cursor.fetchall()
53
- table_count = len(tables)
54
- cursor.close()
55
- conn.close()
56
- return exists, table_count
57
 
58
- return exists, 0
 
 
 
 
 
59
  except Exception as e:
60
- print(f"Помилка перевірки бази employees: {e}")
61
- return False, 0
62
-
63
- @app.route('/')
64
- def index():
65
- # Перевіряємо статус MySQL
66
- status = get_mysql_status()
67
 
68
- # Перевіряємо наявність бази employees
69
- employees_exists, employees_tables = check_employees_db()
70
-
71
- # Отримуємо списки баз даних і таблиць, якщо MySQL онлайн
72
- databases = []
73
- tables = []
74
-
75
- if status["status"] == "online":
76
- try:
77
- conn = get_db_connection()
78
- cursor = conn.cursor()
79
-
80
- # Отримання списку баз даних
81
- cursor.execute("SHOW DATABASES")
82
- databases = [db[0] for db in cursor.fetchall()]
83
-
84
- # Отримання списку таблиць у поточній базі даних
85
- cursor.execute("SHOW TABLES FROM test_db")
86
- tables = [table[0] for table in cursor.fetchall()]
87
-
88
- cursor.close()
89
- conn.close()
90
- except Exception as e:
91
- print(f"Помилка: {e}")
92
-
93
- return render_template('index.html',
94
- status=status,
95
- databases=databases,
96
- tables=tables,
97
- employees_exists=employees_exists,
98
- employees_tables=employees_tables)
99
 
100
  @app.route('/execute', methods=['POST'])
101
  def execute_query():
 
102
  if request.content_type == 'application/json':
103
  data = request.json
104
  query = data.get('query', '')
105
- database = data.get('database', 'test_db')
106
  else:
107
  query = request.form.get('query', '')
108
- database = request.form.get('database', 'test_db')
109
 
110
  if not query:
111
  return jsonify({'error': 'Запит не може бути порожнім'}), 400
112
 
113
  try:
114
- conn = get_db_connection(database)
115
  cursor = conn.cursor(dictionary=True)
116
  cursor.execute(query)
117
 
118
  if query.lower().strip().startswith('select'):
119
  result = cursor.fetchall()
120
  conn.close()
121
-
122
- # Якщо запит з веб-форми, повертаємо HTML
123
- if request.content_type != 'application/json':
124
- return render_template('result.html',
125
- results=result,
126
- query=query,
127
- database=database)
128
- else:
129
- return jsonify({'result': result})
130
  else:
131
  conn.commit()
132
  affected_rows = cursor.rowcount
133
  conn.close()
134
-
135
- # Якщо запит з веб-форми, повертаємо HTML
136
- if request.content_type != 'application/json':
137
- return render_template('result.html',
138
- affected_rows=affected_rows,
139
- query=query,
140
- database=database)
141
- else:
142
- return jsonify({'affected_rows': affected_rows})
143
 
144
  except Exception as e:
145
  error_msg = str(e)
146
- # Якщо запит з веб-форми, повертаємо HTML
147
- if request.content_type != 'application/json':
148
- return render_template('result.html', error=error_msg, query=query, database=database)
149
- else:
150
- return jsonify({'error': error_msg}), 500
151
-
152
- @app.route('/table/<database>/<table_name>')
153
- def show_table(database, table_name):
154
- try:
155
- conn = get_db_connection(database)
156
- cursor = conn.cursor(dictionary=True)
157
-
158
- # Отримуємо структуру таблиці
159
- cursor.execute(f"DESCRIBE `{table_name}`")
160
- columns = cursor.fetchall()
161
-
162
- # Отримуємо дані таблиці
163
- cursor.execute(f"SELECT * FROM `{table_name}` LIMIT 100")
164
- data = cursor.fetchall()
165
-
166
- # Отримуємо загальну кількість рядків у таблиці
167
- cursor.execute(f"SELECT COUNT(*) as count FROM `{table_name}`")
168
- total_count = cursor.fetchone()['count']
169
-
170
- conn.close()
171
- return render_template('table.html',
172
- database=database,
173
- table_name=table_name,
174
- columns=columns,
175
- data=data,
176
- total_count=total_count)
177
- except Exception as e:
178
- return render_template('error.html', error=str(e))
179
 
180
- @app.route('/database/<database>')
181
- def show_database(database):
182
- try:
183
- conn = get_db_connection(database)
184
- cursor = conn.cursor()
185
-
186
- # Отримуємо списки таблиць
187
- cursor.execute(f"SHOW TABLES FROM `{database}`")
188
- tables = [table[0] for table in cursor.fetchall()]
189
-
190
- # Отримуємо розмір кожної таблиці
191
- table_info = []
192
- for table in tables:
193
- cursor.execute(f"SELECT COUNT(*) FROM `{database}`.`{table}`")
194
- count = cursor.fetchone()[0]
195
-
196
- cursor.execute(f"""
197
- SELECT
198
- ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS size_mb
199
- FROM
200
- information_schema.TABLES
201
- WHERE
202
- table_schema = '{database}'
203
- AND table_name = '{table}'
204
- """)
205
- size = cursor.fetchone()[0] or 0
206
-
207
- table_info.append({
208
- 'name': table,
209
- 'count': count,
210
- 'size': size
211
- })
212
-
213
- conn.close()
214
- return render_template('database.html',
215
- database=database,
216
- tables=table_info)
217
- except Exception as e:
218
- return render_template('error.html', error=str(e))
219
 
220
- @app.route('/employees')
221
- def employees_info():
222
  try:
223
- # Перевіряємо чи існує база даних employees
224
- employees_exists, table_count = check_employees_db()
225
-
226
- if not employees_exists:
227
- return render_template('error.html',
228
- error="База даних 'employees' не знайдена",
229
- suggestion="Можливо, виникли проблеми з імпортом бази даних.")
230
 
231
- conn = get_db_connection('employees')
232
- cursor = conn.cursor(dictionary=True)
233
-
234
- # Отримуємо інформацію про таблиці
235
- cursor.execute("""
236
- SELECT
237
- table_name,
238
- table_rows,
239
- ROUND(data_length / 1024 / 1024, 2) as data_size_mb,
240
- ROUND(index_length / 1024 / 1024, 2) as index_size_mb,
241
- ROUND((data_length + index_length) / 1024 / 1024, 2) as total_size_mb
242
- FROM
243
- information_schema.TABLES
244
- WHERE
245
- table_schema = 'employees'
246
- """)
247
- tables_info = cursor.fetchall()
248
-
249
- # Отримуємо загальну кількість співробітників
250
- cursor.execute("SELECT COUNT(*) as count FROM employees")
251
- total_employees = cursor.fetchone()['count']
252
-
253
- # Отримуємо кількість департаментів
254
- cursor.execute("SELECT COUNT(*) as count FROM departments")
255
- total_departments = cursor.fetchone()['count']
256
 
257
- # Отримуємо діапазон дат
258
- cursor.execute("SELECT MIN(hire_date) as min_date, MAX(hire_date) as max_date FROM employees")
259
- date_range = cursor.fetchone()
260
 
261
- # Отримуємо кількість чоловіків і жінок
262
- cursor.execute("""
263
- SELECT
264
- gender,
265
- COUNT(*) as count,
266
- ROUND(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM employees), 2) as percentage
267
- FROM
268
- employees
269
- GROUP BY
270
- gender
271
- """)
272
- gender_stats = cursor.fetchall()
273
 
274
- # Отримуємо топ департаментів за кількістю співробітників
275
- cursor.execute("""
276
- SELECT
277
- d.dept_name,
278
- COUNT(de.emp_no) as employee_count
279
- FROM
280
- departments d
281
- JOIN
282
- dept_emp de ON d.dept_no = de.dept_no
283
- WHERE
284
- de.to_date = '9999-01-01'
285
- GROUP BY
286
- d.dept_name
287
- ORDER BY
288
- employee_count DESC
289
- LIMIT 5
290
- """)
291
- top_departments = cursor.fetchall()
292
 
293
- conn.close()
294
-
295
- return render_template('employees.html',
296
- tables_info=tables_info,
297
- total_employees=total_employees,
298
- total_departments=total_departments,
299
- date_range=date_range,
300
- gender_stats=gender_stats,
301
- top_departments=top_departments)
302
  except Exception as e:
303
- return render_template('error.html', error=str(e))
304
 
305
  if __name__ == '__main__':
306
  # Чекаємо кілька секунд для повного запуску MySQL
307
- time.sleep(2)
308
  app.run(host='0.0.0.0', port=7860)
 
1
  from flask import Flask, request, jsonify, render_template
2
  import mysql.connector
 
3
  import os
4
  import time
5
 
6
+ app = Flask(__name__, template_folder='/templates', static_folder='/static')
7
 
8
+ def get_db_connection():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  try:
10
+ return mysql.connector.connect(
11
+ host="localhost",
12
+ user="test_user",
13
+ password="test_password",
14
+ database="test_db"
15
+ )
 
16
  except Exception as e:
17
+ print(f"Error connecting to MySQL: {e}")
18
+ raise e
19
 
20
+ @app.route('/')
21
+ def index():
22
+ # Перевіряємо статус MySQL
23
+ status = {"status": "offline", "version": "Unknown"}
24
+
25
  try:
 
26
  conn = get_db_connection()
27
  cursor = conn.cursor()
28
+ cursor.execute("SELECT VERSION()")
29
+ version = cursor.fetchone()[0]
30
+ status = {"status": "online", "version": version}
 
31
 
32
+ # Отримуємо списки баз даних
33
+ cursor.execute("SHOW DATABASES")
34
+ databases = [db[0] for db in cursor.fetchall()]
 
 
 
 
 
 
 
35
 
36
+ # Отримуємо списки таблиць
37
+ cursor.execute("SHOW TABLES")
38
+ tables = [table[0] for table in cursor.fetchall()]
39
+
40
+ cursor.close()
41
+ conn.close()
42
  except Exception as e:
43
+ print(f"Error: {e}")
44
+ databases = []
45
+ tables = []
 
 
 
 
46
 
47
+ return render_template('index.html', status=status, databases=databases, tables=tables)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
  @app.route('/execute', methods=['POST'])
50
  def execute_query():
51
+ # Отримуємо дані з форми або JSON
52
  if request.content_type == 'application/json':
53
  data = request.json
54
  query = data.get('query', '')
 
55
  else:
56
  query = request.form.get('query', '')
 
57
 
58
  if not query:
59
  return jsonify({'error': 'Запит не може бути порожнім'}), 400
60
 
61
  try:
62
+ conn = get_db_connection()
63
  cursor = conn.cursor(dictionary=True)
64
  cursor.execute(query)
65
 
66
  if query.lower().strip().startswith('select'):
67
  result = cursor.fetchall()
68
  conn.close()
69
+ return render_template('result.html', results=result, query=query)
 
 
 
 
 
 
 
 
70
  else:
71
  conn.commit()
72
  affected_rows = cursor.rowcount
73
  conn.close()
74
+ return render_template('result.html', affected_rows=affected_rows, query=query)
 
 
 
 
 
 
 
 
75
 
76
  except Exception as e:
77
  error_msg = str(e)
78
+ return render_template('result.html', error=error_msg, query=query)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
+ @app.route('/download_employees', methods=['GET'])
81
+ def download_employees():
82
+ return render_template('download.html')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
 
84
+ @app.route('/import_employees', methods=['POST'])
85
+ def import_employees():
86
  try:
87
+ # Створюємо тимчасову директорію
88
+ os.system("mkdir -p /tmp/employees")
 
 
 
 
 
89
 
90
+ # Завантажуємо базу даних
91
+ os.system("cd /tmp/employees && wget -q https://github.com/datacharmer/test_db/archive/master.zip")
92
+ os.system("cd /tmp/employees && apt-get update && apt-get install -y unzip && unzip -q master.zip")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
+ # Імпортуємо базу даних
95
+ import_result = os.system("cd /tmp/employees/test_db-master && mysql -u root -proot_password < employees.sql")
 
96
 
97
+ # Надаємо права користувачу test_user
98
+ grant_result = os.system("mysql -u root -proot_password -e \"GRANT ALL PRIVILEGES ON employees.* TO 'test_user'@'%';\"")
99
+ os.system("mysql -u root -proot_password -e \"FLUSH PRIVILEGES;\"")
 
 
 
 
 
 
 
 
 
100
 
101
+ # Видаляємо тимчасові файли
102
+ os.system("rm -rf /tmp/employees")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
+ if import_result == 0 and grant_result == 0:
105
+ return render_template('import_success.html')
106
+ else:
107
+ return render_template('import_error.html',
108
+ error=f"Import result: {import_result}, Grant result: {grant_result}")
109
+
 
 
 
110
  except Exception as e:
111
+ return render_template('import_error.html', error=str(e))
112
 
113
  if __name__ == '__main__':
114
  # Чекаємо кілька секунд для повного запуску MySQL
115
+ time.sleep(5)
116
  app.run(host='0.0.0.0', port=7860)
import_success.html ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="uk">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Імпорт завершено</title>
7
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css">
8
+ <style>
9
+ body {
10
+ font-family: Arial, sans-serif;
11
+ padding: 20px;
12
+ }
13
+ .card {
14
+ margin-bottom: 20px;
15
+ border-radius: 8px;
16
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
17
+ }
18
+ </style>
19
+ </head>
20
+ <body>
21
+ <div class="container">
22
+ <div class="d-flex justify-content-between align-items-center mb-4">
23
+ <h1>Імпорт бази даних "Employees"</h1>
24
+ <a href="/" class="btn btn-primary">На головну</a>
25
+ </div>
26
+
27
+ <div class="card">
28
+ <div class="card-header bg-success text-white">
29
+ <h5>Імпорт успішно завершено!</h5>
30
+ </div>
31
+ <div class="card-body">
32
+ <div class="alert alert-success">
33
+ <h4 class="alert-heading">Вітаємо!</h4>
34
+ <p>База даних "Employees" успішно імпортована в MySQL.</p>
35
+ </div>
36
+
37
+ <p>Тепер ви можете виконувати SQL-запити до бази даних "employees". Наприклад:</p>
38
+
39
+ <pre><code>-- Перегляд кількості співробітників
40
+ SELECT COUNT(*) FROM employees.employees;
41
+
42
+ -- Перегляд структури бази даних
43
+ SHOW TABLES FROM employees;
44
+
45
+ -- Перегляд відділів
46
+ SELECT * FROM employees.departments;</code></pre>
47
+
48
+ <a href="/" class="btn btn-primary mt-3">Повернутися на головну сторінку</a>
49
+ </div>
50
+ </div>
51
+ </div>
52
+ </body>
53
+ </html>
start.sh CHANGED
@@ -6,11 +6,11 @@ echo "Запуск MySQL..."
6
 
7
  # Чекаємо, поки MySQL запуститься
8
  echo "Очікування запуску MySQL..."
9
- sleep 20
10
 
11
  # Перевірка, чи запустився MySQL
12
  echo "Перевірка стану MySQL..."
13
- for i in {1..10}; do
14
  if mysqladmin ping -h localhost -u root -proot_password --silent; then
15
  echo "MySQL успішно запущено"
16
  break
@@ -19,45 +19,6 @@ for i in {1..10}; do
19
  sleep 3
20
  done
21
 
22
- # Перевіряємо чи існує база employees
23
- echo "Перевірка наявності бази employees..."
24
- if mysql -u root -proot_password -e "SHOW DATABASES" | grep -q "employees"; then
25
- echo "База даних employees вже існує"
26
- else
27
- echo "База даних employees не знайдена, перевіряємо наявність скрипту імпорту..."
28
-
29
- # Перевіряємо, чи вже було виконано імпорт employees.sql
30
- if [ -f /docker-entrypoint-initdb.d/01-employees.sql ]; then
31
- echo "Файл імпорту знайдено, але база даних не створена. Можливо, виникла помилка під час імпорту."
32
- else
33
- echo "Файл імпорту не знайдено. Спробуємо завантажити базу даних з GitHub..."
34
-
35
- # Завантажуємо employees з GitHub якщо раптом не було завантажено під час збірки
36
- mkdir -p /tmp/employees && \
37
- cd /tmp/employees && \
38
- git clone https://github.com/datacharmer/test_db.git && \
39
- cd test_db && \
40
-
41
- # Імпортуємо базу даних
42
- echo "Імпорт бази даних employees..."
43
- mysql -u root -proot_password < employees.sql
44
-
45
- if [ $? -eq 0 ]; then
46
- echo "База даних employees успішно імпортована!"
47
- else
48
- echo "Помилка під час імпорту бази даних employees!"
49
- fi
50
-
51
- cd / && \
52
- rm -rf /tmp/employees
53
- fi
54
- fi
55
-
56
- # Надаємо права користувачу test_user на базу employees
57
- echo "Надання прав доступу до бази employees користувачу test_user..."
58
- mysql -u root -proot_password -e "GRANT ALL PRIVILEGES ON employees.* TO 'test_user'@'%';"
59
- mysql -u root -proot_password -e "FLUSH PRIVILEGES;"
60
-
61
  # Запуск Flask API
62
  echo "Запуск API на порту 7860..."
63
  python3 /app.py
 
6
 
7
  # Чекаємо, поки MySQL запуститься
8
  echo "Очікування запуску MySQL..."
9
+ sleep 15
10
 
11
  # Перевірка, чи запустився MySQL
12
  echo "Перевірка стану MySQL..."
13
+ for i in {1..5}; do
14
  if mysqladmin ping -h localhost -u root -proot_password --silent; then
15
  echo "MySQL успішно запущено"
16
  break
 
19
  sleep 3
20
  done
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  # Запуск Flask API
23
  echo "Запуск API на порту 7860..."
24
  python3 /app.py
templates/download.html ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="uk">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Завантаження бази даних Employees</title>
7
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css">
8
+ <style>
9
+ body {
10
+ font-family: Arial, sans-serif;
11
+ padding: 20px;
12
+ }
13
+ .card {
14
+ margin-bottom: 20px;
15
+ border-radius: 8px;
16
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
17
+ }
18
+ </style>
19
+ </head>
20
+ <body>
21
+ <div class="container">
22
+ <div class="d-flex justify-content-between align-items-center mb-4">
23
+ <h1>Завантаження бази даних "Employees"</h1>
24
+ <a href="/" class="btn btn-primary">Назад</a>
25
+ </div>
26
+
27
+ <div class="card mb-4">
28
+ <div class="card-header bg-primary text-white">
29
+ <h5>Імпорт бази даних</h5>
30
+ </div>
31
+ <div class="card-body">
32
+ <p>Ви збираєтеся завантажити та імпортувати базу даних "Employees" з репозиторію: <a href="https://github.com/datacharmer/test_db" target="_blank">https://github.com/datacharmer/test_db</a></p>
33
+
34
+ <div class="alert alert-warning">
35
+ <h4 class="alert-heading">Увага!</h4>
36
+ <p>Процес імпорту може зайняти кілька хвилин. Будь ласка, не закривайте цю сторінку і не перезавантажуйте браузер під час імпорту.</p>
37
+ </div>
38
+
39
+ <form action="/import_employees" method="post">
40
+ <button type="submit" class="btn btn-success btn-lg">Почати імпорт</button>
41
+ </form>
42
+ </div>
43
+ </div>
44
+
45
+ <div class="card">
46
+ <div class="card-header bg-info text-white">
47
+ <h5>Інформація про базу даних</h5>
48
+ </div>
49
+ <div class="card-body">
50
+ <p>База даних "Employees" - це навчальна база даних, яка містить приблизно 4 мільйони записів, розподілених по декількох таблицях:</p>
51
+ <ul>
52
+ <li><strong>employees</strong> - дані про співробітників</li>
53
+ <li><strong>departments</strong> - дані про відділи</li>
54
+ <li><strong>dept_emp</strong> - зв'язок між співробітниками і відділами</li>
55
+ <li><strong>dept_manager</strong> - дані про менеджерів відділів</li>
56
+ <li><strong>titles</strong> - посади співробітників</li>
57
+ <li><strong>salaries</strong> - дані про зарплати</li>
58
+ </ul>
59
+ <p>Після імпорту ви зможете виконувати SQL-запити до цієї бази даних для виконання наступних практичних завдань.</p>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </body>
64
+ </html>
templates/error.html CHANGED
@@ -3,20 +3,50 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Помилка</title>
7
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css">
8
- <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
 
 
 
 
 
 
 
 
 
 
9
  </head>
10
  <body>
11
- <div class="container mt-4">
12
  <div class="d-flex justify-content-between align-items-center mb-4">
13
- <h1>Помилка</h1>
14
- <a href="/" class="btn btn-primary">Назад</a>
15
  </div>
16
 
17
- <div class="alert alert-danger">
18
- <h4 class="alert-heading">Виникла помилка!</h4>
19
- <p>{{ error }}</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  </div>
21
  </div>
22
  </body>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Помилка імпорту</title>
7
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css">
8
+ <style>
9
+ body {
10
+ font-family: Arial, sans-serif;
11
+ padding: 20px;
12
+ }
13
+ .card {
14
+ margin-bottom: 20px;
15
+ border-radius: 8px;
16
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
17
+ }
18
+ </style>
19
  </head>
20
  <body>
21
+ <div class="container">
22
  <div class="d-flex justify-content-between align-items-center mb-4">
23
+ <h1>Імпорт бази даних "Employees"</h1>
24
+ <a href="/" class="btn btn-primary">На головну</a>
25
  </div>
26
 
27
+ <div class="card">
28
+ <div class="card-header bg-danger text-white">
29
+ <h5>Помилка імпорту</h5>
30
+ </div>
31
+ <div class="card-body">
32
+ <div class="alert alert-danger">
33
+ <h4 class="alert-heading">Виникла помилка!</h4>
34
+ <p>Не вдалося імпортувати базу даних "Employees".</p>
35
+ <p><strong>Помилка:</strong> {{ error }}</p>
36
+ </div>
37
+
38
+ <p>Можливі причини помилки:</p>
39
+ <ul>
40
+ <li>Проблеми з підключенням до сервера GitHub</li>
41
+ <li>Недостатньо місця на диску</li>
42
+ <li>Помилка при виконанні SQL-скрипту</li>
43
+ <li>Недостатньо прав доступу</li>
44
+ </ul>
45
+
46
+ <p>Ви можете спробувати виконати імпорт ще раз або звернутися до адміністратора системи.</p>
47
+
48
+ <a href="/download_employees" class="btn btn-primary mt-3">Спробувати ще раз</a>
49
+ </div>
50
  </div>
51
  </div>
52
  </body>
templates/index.html CHANGED
@@ -5,33 +5,26 @@
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>MySQL 8.0.22 для навчальних цілей</title>
7
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css">
8
- <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  </head>
10
  <body>
11
- <div class="container mt-4">
12
- <h1 class="mb-4">MySQL Сервер 8.0.22 для навчальних цілей</h1>
13
-
14
- <!-- Навігація -->
15
- <div class="card mb-4">
16
- <div class="card-header bg-dark text-white">
17
- <h5>Навігація</h5>
18
- </div>
19
- <div class="card-body">
20
- <div class="row">
21
- <div class="col-md-4 mb-2">
22
- <a href="#sql-console" class="btn btn-primary w-100">SQL Консоль</a>
23
- </div>
24
- <div class="col-md-4 mb-2">
25
- <a href="/database/test_db" class="btn btn-secondary w-100">База даних test_db</a>
26
- </div>
27
- {% if employees_exists %}
28
- <div class="col-md-4 mb-2">
29
- <a href="/employees" class="btn btn-success w-100">База даних employees</a>
30
- </div>
31
- {% endif %}
32
- </div>
33
- </div>
34
- </div>
35
 
36
  <!-- Статус сервера -->
37
  <div class="card mb-4">
@@ -41,64 +34,31 @@
41
  <div class="card-body">
42
  {% if status.status == 'online' %}
43
  <p><strong>Версія MySQL:</strong> {{ status.version }}</p>
44
-
45
- <!-- Інформація про базу employees -->
46
- {% if employees_exists %}
47
- <div class="alert alert-success">
48
- <h5>База даних "Employees" успішно імпортована!</h5>
49
- <p>Кількість таблиць: {{ employees_tables }}</p>
50
- <a href="/employees" class="btn btn-sm btn-outline-success">Переглянути деталі</a>
51
- </div>
52
- {% else %}
53
- <div class="alert alert-warning">
54
- <h5>База даних "Employees" не знайдена</h5>
55
- <p>Можливо, виникли проблеми при імпорті бази даних.</p>
56
- </div>
57
- {% endif %}
58
  {% else %}
59
  <p class="text-danger"><strong>Помилка:</strong> {{ status.error }}</p>
60
  {% endif %}
61
  </div>
62
  </div>
63
 
64
- <!-- Параметри підключення -->
65
  <div class="card mb-4">
66
- <div class="card-header bg-info text-white">
67
- <h5>Параметри підключення через API</h5>
68
  </div>
69
  <div class="card-body">
70
- <p>Для виконання SQL запитів надсилайте POST запити на <code>/execute</code> з JSON тілом:</p>
71
- <pre><code>{
72
- "query": "ваш SQL запит",
73
- "database": "назва_бази_даних" // опціонально, за замовчуванням "test_db"
74
- }</code></pre>
75
- <p>Приклад використання з curl:</p>
76
- <pre><code>curl -X POST https://mysql-itud-docsa.hf.space/execute \
77
- -H "Content-Type: application/json" \
78
- -d '{"query":"SELECT * FROM test", "database":"test_db"}'</code></pre>
79
-
80
- <p>Для роботи з базою даних employees:</p>
81
- <pre><code>curl -X POST https://mysql-itud-docsa.hf.space/execute \
82
- -H "Content-Type: application/json" \
83
- -d '{"query":"SELECT * FROM employees LIMIT 5", "database":"employees"}'</code></pre>
84
  </div>
85
  </div>
86
 
87
  <!-- SQL Консоль -->
88
- <div class="card mb-4" id="sql-console">
89
  <div class="card-header bg-primary text-white">
90
  <h5>SQL Консоль</h5>
91
  </div>
92
  <div class="card-body">
93
  <form action="/execute" method="post">
94
- <div class="mb-3">
95
- <label for="database" class="form-label">Виберіть базу даних:</label>
96
- <select class="form-select" id="database" name="database">
97
- {% for db in databases %}
98
- <option value="{{ db }}" {% if db == 'test_db' %}selected{% endif %}>{{ db }}</option>
99
- {% endfor %}
100
- </select>
101
- </div>
102
  <div class="mb-3">
103
  <label for="query" class="form-label">Введіть SQL запит:</label>
104
  <textarea class="form-control" id="query" name="query" rows="6" placeholder="SELECT * FROM test"></textarea>
@@ -110,7 +70,6 @@
110
 
111
  <!-- Список баз даних і таблиць -->
112
  <div class="row">
113
- <!-- Бази даних -->
114
  <div class="col-md-6">
115
  <div class="card mb-4">
116
  <div class="card-header bg-secondary text-white">
@@ -120,22 +79,16 @@
120
  {% if databases %}
121
  <ul class="list-group">
122
  {% for db in databases %}
123
- <li class="list-group-item">
124
- <a href="/database/{{ db }}">{{ db }}</a>
125
- {% if db == 'employees' %}
126
- <span class="badge bg-success">Навчальна база</span>
127
- {% endif %}
128
- </li>
129
  {% endfor %}
130
  </ul>
131
  {% else %}
132
- <p>Немає доступних баз даних або MySQL не запущено</p>
133
  {% endif %}
134
  </div>
135
  </div>
136
  </div>
137
 
138
- <!-- Таблиці -->
139
  <div class="col-md-6">
140
  <div class="card mb-4">
141
  <div class="card-header bg-secondary text-white">
@@ -145,27 +98,24 @@
145
  {% if tables %}
146
  <ul class="list-group">
147
  {% for table in tables %}
148
- <li class="list-group-item">
149
- <a href="/table/test_db/{{ table }}">{{ table }}</a>
150
- </li>
151
  {% endfor %}
152
  </ul>
153
  {% else %}
154
- <p>Немає доступних таблиць або MySQL не запущено</p>
155
  {% endif %}
156
  </div>
157
  </div>
158
  </div>
159
  </div>
160
 
161
- <!-- Практичне завдання -->
162
  <div class="card mb-4">
163
  <div class="card-header bg-dark text-white">
164
- <h5>Практичне заняття №1</h5>
165
  </div>
166
  <div class="card-body">
167
- <h5>Завдання 1.3</h5>
168
- <p>Для виконання тестового завдання виконайте наступний SQL скрипт:</p>
169
  <pre><code>-- Нереформатований скрипт
170
  create table if not exists test (numbers int, words varchar (10));
171
 
@@ -175,15 +125,6 @@ CREATE TABLE IF NOT EXISTS test (
175
  words VARCHAR(10) -- Поле для текстових значень максимум 10 символів
176
  );</code></pre>
177
  <button class="btn btn-outline-primary" onclick="copyToQueryConsole('create table if not exists test (numbers int, words varchar (10));')">Копіювати до консолі</button>
178
-
179
- <h5 class="mt-4">Завдання із використанням - Бази даних "Employees"</h5>
180
- <p>Завдання 1.2 виконане автоматично - базу даних "Employees" вже імпортовано в середовище MySQL.</p>
181
- {% if employees_exists %}
182
- <p>Ви можете переглянути структуру та дані бази за посиланням:</p>
183
- <a href="/employees" class="btn btn-success">Переглянути базу "Employees"</a>
184
- {% else %}
185
- <p class="text-danger">На жаль, база даних "Employees" не була успішно імпортована.</p>
186
- {% endif %}
187
  </div>
188
  </div>
189
  </div>
 
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>MySQL 8.0.22 для навчальних цілей</title>
7
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css">
8
+ <style>
9
+ body {
10
+ font-family: Arial, sans-serif;
11
+ padding: 20px;
12
+ }
13
+ .card {
14
+ margin-bottom: 20px;
15
+ border-radius: 8px;
16
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
17
+ }
18
+ pre {
19
+ background-color: #f8f9fa;
20
+ padding: 15px;
21
+ border-radius: 5px;
22
+ }
23
+ </style>
24
  </head>
25
  <body>
26
+ <div class="container">
27
+ <h1 class="mb-4">MySQL 8.0.22 для навчальних цілей</h1>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
  <!-- Статус сервера -->
30
  <div class="card mb-4">
 
34
  <div class="card-body">
35
  {% if status.status == 'online' %}
36
  <p><strong>Версія MySQL:</strong> {{ status.version }}</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  {% else %}
38
  <p class="text-danger"><strong>Помилка:</strong> {{ status.error }}</p>
39
  {% endif %}
40
  </div>
41
  </div>
42
 
43
+ <!-- Завдання 1.2 (Імпорт бази Employees) -->
44
  <div class="card mb-4">
45
+ <div class="card-header bg-primary text-white">
46
+ <h5>Завдання 1.2 - Імпорт бази даних "Employees"</h5>
47
  </div>
48
  <div class="card-body">
49
+ <p>Для виконання завдання 1.2 вам потрібно завантажити і імпортувати базу даних "Employees".</p>
50
+ <a href="/download_employees" class="btn btn-success">Завантажити і імпортувати базу Employees</a>
51
+ <p class="mt-3">Процес імпорту може зайняти кілька хвилин. Після успішного імпорту база даних "employees" з'явиться у списку доступних баз даних.</p>
 
 
 
 
 
 
 
 
 
 
 
52
  </div>
53
  </div>
54
 
55
  <!-- SQL Консоль -->
56
+ <div class="card mb-4">
57
  <div class="card-header bg-primary text-white">
58
  <h5>SQL Консоль</h5>
59
  </div>
60
  <div class="card-body">
61
  <form action="/execute" method="post">
 
 
 
 
 
 
 
 
62
  <div class="mb-3">
63
  <label for="query" class="form-label">Введіть SQL запит:</label>
64
  <textarea class="form-control" id="query" name="query" rows="6" placeholder="SELECT * FROM test"></textarea>
 
70
 
71
  <!-- Список баз даних і таблиць -->
72
  <div class="row">
 
73
  <div class="col-md-6">
74
  <div class="card mb-4">
75
  <div class="card-header bg-secondary text-white">
 
79
  {% if databases %}
80
  <ul class="list-group">
81
  {% for db in databases %}
82
+ <li class="list-group-item">{{ db }}</li>
 
 
 
 
 
83
  {% endfor %}
84
  </ul>
85
  {% else %}
86
+ <p>Немає доступних баз даних</p>
87
  {% endif %}
88
  </div>
89
  </div>
90
  </div>
91
 
 
92
  <div class="col-md-6">
93
  <div class="card mb-4">
94
  <div class="card-header bg-secondary text-white">
 
98
  {% if tables %}
99
  <ul class="list-group">
100
  {% for table in tables %}
101
+ <li class="list-group-item">{{ table }}</li>
 
 
102
  {% endfor %}
103
  </ul>
104
  {% else %}
105
+ <p>Немає доступних таблиць</p>
106
  {% endif %}
107
  </div>
108
  </div>
109
  </div>
110
  </div>
111
 
112
+ <!-- Завдання 1.3 -->
113
  <div class="card mb-4">
114
  <div class="card-header bg-dark text-white">
115
+ <h5>Завдання 1.3</h5>
116
  </div>
117
  <div class="card-body">
118
+ <p>Для виконання завдання 1.3 виконайте наступний SQL скрипт:</p>
 
119
  <pre><code>-- Нереформатований скрипт
120
  create table if not exists test (numbers int, words varchar (10));
121
 
 
125
  words VARCHAR(10) -- Поле для текстових значень максимум 10 символів
126
  );</code></pre>
127
  <button class="btn btn-outline-primary" onclick="copyToQueryConsole('create table if not exists test (numbers int, words varchar (10));')">Копіювати до консолі</button>
 
 
 
 
 
 
 
 
 
128
  </div>
129
  </div>
130
  </div>
templates/result.html CHANGED
@@ -5,10 +5,25 @@
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Результат SQL запиту</title>
7
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css">
8
- <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  </head>
10
  <body>
11
- <div class="container mt-4">
12
  <div class="d-flex justify-content-between align-items-center mb-4">
13
  <h1>Результат запиту</h1>
14
  <a href="/" class="btn btn-primary">Назад</a>
 
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Результат SQL запиту</title>
7
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css">
8
+ <style>
9
+ body {
10
+ font-family: Arial, sans-serif;
11
+ padding: 20px;
12
+ }
13
+ .card {
14
+ margin-bottom: 20px;
15
+ border-radius: 8px;
16
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
17
+ }
18
+ pre {
19
+ background-color: #f8f9fa;
20
+ padding: 15px;
21
+ border-radius: 5px;
22
+ }
23
+ </style>
24
  </head>
25
  <body>
26
+ <div class="container">
27
  <div class="d-flex justify-content-between align-items-center mb-4">
28
  <h1>Результат запиту</h1>
29
  <a href="/" class="btn btn-primary">Назад</a>