Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions lab1/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import argparse
import re
from collections import defaultdict
import os

def read_file(file_path: str) -> str:
"""
Читает содержимое файла.
"""
try:
with open(file_path, "r", encoding="utf-8") as file:
return file.read()
except FileNotFoundError as e:
raise FileNotFoundError(f"Файл не найден: {file_path}") from e
except IOError as e:
raise IOError(f"Ошибка при ччтении файла {file_path}: {e}") from e

def parse_person_data(content):
"""Парсит данные о людях из текста файла"""
# Разбиваем на записи (каждая запись - 6 строк)
lines = content.strip().split('\n')
people = []

# Обрабатываем записи блоками по 6 строк
for i in range(0, len(lines), 6):
if i + 5 < len(lines):
person = {
'surname': lines[i].strip(),
'name': lines[i+1].strip(),
'gender': lines[i+2].strip(),
'birth_date': lines[i+3].strip(),
'contact': lines[i+4].strip(),
'city': lines[i+5].strip()
}
people.append(person)

return people

def count_cities(people):
"""Подсчитывает количество людей по городам"""
city_count = defaultdict(int)

for person in people:
city = person['city']
if city: # Проверяем, что город не пустой
city_count[city] += 1

return city_count

def save_statistics(city_count, output_file='city_statistics.txt'):
"""Сохраняет статистику по городам в файл"""
# Сортируем сначала по количеству (убывание), затем по названию города (по алфавиту)
sorted_cities = sorted(city_count.items(),
key=lambda x: (-x[1], x[0]))

with open(output_file, 'w', encoding='utf-8') as f:
for city, count in sorted_cities:
f.write(f"{city}: {count}\n")

return sorted_cities

def print_statistics(sorted_cities):
"""Выводит статистику на экран"""
print("\nСтатистика по городам:")
print("-" * 30)
for city, count in sorted_cities:
print(f"{city}: {count}")
print("-" * 30)

def main():
# Настраиваем парсер аргументов командной строки
parser = argparse.ArgumentParser(
description='Статистика по городам из файла с анкетами людей'
)
parser.add_argument(
'filename',
help='Имя файла с данными (например, data.txt)'
)
parser.add_argument(
'-o', '--output',
default='city_statistics.txt',
help='Имя выходного файла для статистики (по умолчанию: city_statistics.txt)'
)

# Парсим аргументы
args = parser.parse_args()

# Проверяем существование файла
if not os.path.exists(args.filename):
print(f"Ошибка: Файл '{args.filename}' не найден!")
return

try:
# Читаем файл
with open(args.filename, 'r', encoding='utf-8') as file:
content = file.read()

print(f"Файл '{args.filename}' успешно загружен.")

# Парсим данные о людях
people = parse_person_data(content)
print(f"Найдено записей: {len(people)}")

# Подсчитываем статистику по городам
city_count = count_cities(people)

# Сохраняем и выводим статистику
sorted_cities = save_statistics(city_count, args.output)
print_statistics(sorted_cities)

print(f"\nСтатистика сохранена в файл: {args.output}")

except Exception as e:
print(f"Произошла ошибка: {e}")

if __name__ == "__main__":
main()
35 changes: 35 additions & 0 deletions lab2/annotation.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
absolute_path,relative_path
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\001_Hip_Hop_02.mp3,downloads\001_Hip_Hop_02.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\002_Sun_and_His_Daughter.mp3,downloads\002_Sun_and_His_Daughter.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\003_Hazy_After_Hours.mp3,downloads\003_Hazy_After_Hours.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\004_Tech_House_vibes.mp3,downloads\004_Tech_House_vibes.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\005_A_Very_Happy_Christmas.mp3,downloads\005_A_Very_Happy_Christmas.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\006_Driving_Ambition.mp3,downloads\006_Driving_Ambition.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\007_Serene_View.mp3,downloads\007_Serene_View.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\008_Beautiful_Dream.mp3,downloads\008_Beautiful_Dream.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\009_Silent_Descent.mp3,downloads\009_Silent_Descent.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\010_Fright_Night.mp3,downloads\010_Fright_Night.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\011_Pop_05.mp3,downloads\011_Pop_05.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\012_Piano_Horror.mp3,downloads\012_Piano_Horror.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\013_Romantic.mp3,downloads\013_Romantic.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\014_Cat_Walk.mp3,downloads\014_Cat_Walk.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\015_Gimme_that_Groove.mp3,downloads\015_Gimme_that_Groove.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\016_Wedding_01.mp3,downloads\016_Wedding_01.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\017_Epical_Drums_01.mp3,downloads\017_Epical_Drums_01.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\018_Discover.mp3,downloads\018_Discover.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\019_Valley_Sunset.mp3,downloads\019_Valley_Sunset.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\020_CBPD.mp3,downloads\020_CBPD.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\021_Cant_Get_You_Off_My_Mind.mp3,downloads\021_Cant_Get_You_Off_My_Mind.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\022_Praise_the_Lord.mp3,downloads\022_Praise_the_Lord.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\023_Games_Worldbeat.mp3,downloads\023_Games_Worldbeat.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\024_Sports_Highlights.mp3,downloads\024_Sports_Highlights.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\025_Island_Beat.mp3,downloads\025_Island_Beat.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\026_Dirty_Thinkin.mp3,downloads\026_Dirty_Thinkin.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\027_Spirit_in_the_Woods.mp3,downloads\027_Spirit_in_the_Woods.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\028_Complicated.mp3,downloads\028_Complicated.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\029_Relaxing_in_Nature.mp3,downloads\029_Relaxing_in_Nature.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\030_Hollidays.mp3,downloads\030_Hollidays.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\031_Romantic_05.mp3,downloads\031_Romantic_05.mp3
C:\Users\Андрей\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\032_Villa_Penthouse.mp3,downloads\032_Villa_Penthouse.mp3
C:\Users\neon4\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\033_Forest_Treasure.mp3,downloads\033_Forest_Treasure.mp3
C:\Users\neon4\OneDrive\Desktop\uni\2 year\app prog\lab2\downloads\034_Romantic_01.mp3,downloads\034_Romantic_01.mp3
16 changes: 16 additions & 0 deletions lab2/annotation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import csv
import os
from typing import List

def create_annotation(file_paths: List[str], csv_path: str) -> None:
"""
Создаёт CSV-аннотацию для списка файлов.
:param file_paths: список абсолютных путей к файлам
:param csv_path: путь к CSV файлу для сохранения аннотации
"""
with open(csv_path, "w", newline="", encoding="utf-8") as file:
writer = csv.writer(file)
writer.writerow(["absolute_path", "relative_path"])
for path in file_paths:
rel = os.path.relpath(path)
writer.writerow([os.path.abspath(path), rel])
131 changes: 131 additions & 0 deletions lab2/downloader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import os
import re
import requests
from bs4 import BeautifulSoup
from typing import List
from annotation import create_annotation

class MusicDownloader:
"""
Класс для парсинга и скачивания аудиофайлов с mixkit.co по длительности.
"""

BASE_URL = "https://mixkit.co/free-stock-music/"

def __init__(self, download_dir: str):
"""
Инициализация загрузчика, подготовка директории и HTTP-сессии.
:param download_dir: путь к директории для сохранения аудиофайлов
"""
self.download_dir = download_dir
os.makedirs(download_dir, exist_ok=True)
self.session = requests.Session()
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
})

def parse_duration(self, duration_str: str) -> int:
"""
Преобразует строку 'm:ss' в секунды.
:param duration_str: строка длительности
:return: длительность в секундах, либо 0 при ошибке"""
try:
minutes, seconds = map(int, duration_str.strip().split(":"))
return minutes * 60 + seconds
except Exception:
return 0

def get_tracks_from_page(self, min_sec: int, max_sec: int) -> List[dict]:
"""
Парсит страницу mixkit и возвращает список треков с прямыми ссылками и длительностью.
:param min_sec: минимальная длительность трека в секундах
:param max_sec: максимальная длительность трека в секундах
:return: список треков в виде словарей (title, duration, url)
"""
url = self.BASE_URL
response = self.session.get(url)
if not response.ok:
return []

soup = BeautifulSoup(response.text, "html.parser")
items = soup.select("div.item-grid__item")
tracks = []

for item in items:
title_el = item.select_one("h2.item-grid-card__title")
duration_el = item.select_one('div[data-test-id="duration"]')
player_el = item.select_one('div[data-test-id="audio-player"]')

if not all([title_el, duration_el, player_el]):
continue

title = title_el.get_text(strip=True)
duration = self.parse_duration(duration_el.get_text(strip=True))
mp3_url = player_el.get("data-audio-player-preview-url-value")

if not mp3_url or not mp3_url.startswith("http"):
mp3_url = f"https://mixkit.co{mp3_url}"

if min_sec <= duration <= max_sec:
tracks.append({
"title": title,
"duration": duration,
"url": mp3_url
})

return tracks

def download_file(self, url: str, save_path: str) -> bool:
"""
Скачивает файл по ссылке и сохраняет на диск.
:param url: прямая ссылка на MP3-файл
:param save_path: путь сохранения файла
:return: True если успешно, иначе False
"""
try:
response = self.session.get(url, stream=True, timeout=30)
response.raise_for_status()
with open(save_path, "wb") as f:
for chunk in response.iter_content(8192):
if chunk:
f.write(chunk)
return True
except Exception:
if os.path.exists(save_path):
os.remove(save_path)
return False

def download_music(self, count: int, min_sec: int, max_sec: int, csv_path: str) -> None:
"""
Ищет треки по длительности, скачивает нужное количество и создаёт аннотацию.
:param count: количество треков для скачивания
:param min_sec: минимальная длительность трека в секундах
:param max_sec: максимальная длительность трека в секундах
:param csv_path: путь сохранения CSV-аннотации
:return: None
"""
print(f"🔍 Поиск треков длительностью от {min_sec} до {max_sec} секунд...")

found_tracks = self.get_tracks_from_page(min_sec, max_sec)

found_tracks = found_tracks[:count]

if not found_tracks:
print("⚠️ Не найдено треков подходящей длительности.")
create_annotation([], csv_path)
return

print(f"🎧 Найдено {len(found_tracks)} треков. Начинаем загрузку...")

downloaded_paths = []
for i, track in enumerate(found_tracks, 1):
safe_title = re.sub(r"[^\w\s-]", "", track["title"]).replace(" ", "_")[:80]
save_path = os.path.join(self.download_dir, f"{i:03d}_{safe_title}.mp3")
if self.download_file(track["url"], save_path):
print(f" ✅ [{i}] {track['title']} ({track['duration']} сек)")
downloaded_paths.append(save_path)
else:
print(f" ❌ Ошибка скачивания: {track['title']}")

create_annotation(downloaded_paths, csv_path)
print(f"🧾 Аннотация создана: {csv_path}")
29 changes: 29 additions & 0 deletions lab2/iterator_module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import csv

class FileIterator:
"""
Итератор для обхода путей из CSV-файла аннотации.
"""
def __init__(self, csv_path: str):
self.csv_path = csv_path
self._rows = []
self._index = 0

try:
with open(csv_path, newline="", encoding="utf-8") as file:
reader = csv.reader(file)
next(reader, None)
self._rows = [row[0] for row in reader if row]
except FileNotFoundError:
print(f"Файл аннотации не найден: {csv_path}")
self._rows = []

def __iter__(self):
return self

def __next__(self) -> str:
if self._index >= len(self._rows):
raise StopIteration
path = self._rows[self._index]
self._index += 1
return path
29 changes: 29 additions & 0 deletions lab2/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import argparse
from downloader import MusicDownloader
from iterator_module import FileIterator

def main() -> None:
parser = argparse.ArgumentParser(description="Скачивание музыки с mixkit.co по длительности")
parser.add_argument("--folder", type=str, default="./downloads", help="Папка для сохранения треков")
parser.add_argument("--csv", type=str, default="./annotation.csv", help="CSV-аннотация")
parser.add_argument("--count", type=int, default=50, help="Количество треков для скачивания")
parser.add_argument("--min_duration", type=int, default=30, help="Минимальная длительность (сек)")
parser.add_argument("--max_duration", type=int, default=180, help="Максимальная длительность (сек)")
args = parser.parse_args()

print(f"🎵 Начинаем скачивание до {args.count} треков длительностью от {args.min_duration} до {args.max_duration} сек...")

downloader = MusicDownloader(args.folder)
downloader.download_music(
count=args.count,
min_sec=args.min_duration,
max_sec=args.max_duration,
csv_path=args.csv
)

print(f"🔁 Проверка итератора:")
for path in FileIterator(args.csv):
print(" ", path)

if __name__ == "__main__":
main()
27 changes: 27 additions & 0 deletions lab3/audio_processing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import numpy as np
import soundfile as sf
from typing import Tuple

def read_audio_file(file_path: str) -> Tuple[np.ndarray, int]:
"""Читает аудиофайл mp3 или wav."""
audio_data, sample_rate = sf.read(file_path)
return audio_data, sample_rate

def stereo_to_mono(audio_data: np.ndarray) -> np.ndarray:
"""Приводит стерео аудио к моно."""
if audio_data.ndim == 1:
return audio_data
return audio_data.mean(axis=1)

def limit_amplitude(audio_data: np.ndarray, threshold: float) -> np.ndarray:
"""Ограничивает амплитуду выше порога threshold."""
limited_audio = np.copy(audio_data)
limited_audio[limited_audio > threshold] = threshold
limited_audio[limited_audio < -threshold] = -threshold
return limited_audio

def save_audio(file_path: str, audio_data: np.ndarray, sample_rate: int) -> None:
"""Сохраняет аудио в mp3 или wav, создавая директории."""
import os
os.makedirs(os.path.dirname(file_path), exist_ok=True)
sf.write(file_path, audio_data, sample_rate)
Loading