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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node/node_modules
/python/
32 changes: 32 additions & 0 deletions python/GornyhIS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## Горных ИС

🔍 Анализ проблемы в исходном коде

## Повторное чтение файла:

Методы where и find_by каждый раз открывают и читают CSV.
Рефакторинг: добавлено кэширование через _load_data.

## Дублирование кода:

Одинаковые блоки фильтрации и маппинга в where и find_by.
Рефакторинг: вынесена общая логика фильтрации и маппинга.

## обработка find_by:

find_by возвращал первую найденную запись по одному полю,
но не учитывал комбинации условий.
Теперь find_by использует where, что гарантирует консистентность.

## Отсутствие обработки кодировки:

Добавлен encoding="utf-8" для надежности.

## Стиль и архитектура:

Использование @staticmethod вместо @classmethod.
RecordNotFound определён внизу, лучше его вынести наверх.

## Нет проверки на пустые результаты в find_by:

Теперь корректно выбрасывается исключение RecordNotFound.
66 changes: 66 additions & 0 deletions python/funding_ref.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import csv

class RecordNotFound(Exception):
"""Исключение, выбрасываемое, когда запись не найдена."""
pass

class FundingRaised:
# Кэшируем данные CSV, чтобы не читать файл каждый раз
_cached_data = None

@classmethod
def _load_data(cls):
"""Загружает данные из CSV один раз и кэширует их."""
if cls._cached_data is None:
with open("../startup_funding.csv", "rt", encoding="utf-8") as csvfile:
reader = csv.reader(csvfile, delimiter=',', quotechar='"')
next(reader) # Пропускаем заголовок
cls._cached_data = [row for row in reader]
return cls._cached_data

@staticmethod
def _map_row(row):
"""Преобразует строку CSV в словарь с нужными ключами."""
keys = [
'permalink', 'company_name', 'number_employees', 'category',
'city', 'state', 'funded_date', 'raised_amount',
'raised_currency', 'round'
]
return dict(zip(keys, row))

@classmethod
def where(cls, options=None):
"""
Возвращает список всех записей, соответствующих условиям.
Поддерживает фильтрацию по: company_name, city, state, round.
"""
if options is None:
options = {}

data = cls._load_data()
filtered_data = data

# Фильтрация по каждому полю
filters = {
'company_name': 1,
'city': 4,
'state': 5,
'round': 9
}

for key, index in filters.items():
if key in options:
filtered_data = [row for row in filtered_data if row[index] == options[key]]

return [cls._map_row(row) for row in filtered_data]

@classmethod
def find_by(cls, options):
"""
Возвращает первую запись, соответствующую условиям.
Выбрасывает RecordNotFound, если ничего не найдено.
"""
result = cls.where(options)
if not result:
raise RecordNotFound(f"Запись с параметрами {options} не найдена.")
return result[0]
63 changes: 63 additions & 0 deletions python/test_ref.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from funding_ref import FundingRaised


def test_where_returns_events():
assert len(FundingRaised.where({'company_name': 'Facebook'})) == 7


def test_where_returns_correct_keys():
row = FundingRaised.where({'company_name': 'Facebook'})[0]
keys = ['permalink', 'company_name', 'number_employees', 'category', 'city', 'state', 'funded_date',
'raised_amount', 'raised_currency', 'round']
values = ['facebook', 'Facebook', '450', 'web', 'Palo Alto', 'CA', '1-Sep-04', '500000', 'USD', 'angel']
for i in range(0, len(keys)):
assert row[keys[i]] == values[i]


def test_where_returns_events_by_city():
assert len(FundingRaised.where({'city': 'Tempe'})) == 3


def test_where_returns_events_by_state():
assert len(FundingRaised.where({'state': 'CA'})) == 873


def test_where_returns_events_by_company():
assert len(FundingRaised.where({'company_name': 'Facebook', 'round': 'a'})) == 1


def test_where_returns_events_by_type():
assert len(FundingRaised.where({'round': 'a'})) == 582


def test_where_returns_no_events():
assert len(FundingRaised.where({'company_name': 'NotFacebook'})) == 0


def test_find_by_event_by_company_name():
row = FundingRaised.find_by({'company_name': 'Facebook'})
keys = ['permalink', 'company_name', 'number_employees', 'category', 'city', 'state', 'funded_date',
'raised_amount', 'raised_currency', 'round']
values = ['facebook', 'Facebook', '450', 'web', 'Palo Alto', 'CA', '1-Sep-04', '500000', 'USD', 'angel']
for i in range(0, len(keys)):
assert row[keys[i]] == values[i]


def test_find_by_event_by_state():
row = FundingRaised.find_by({'state': 'CA'})
keys = ['permalink', 'company_name', 'number_employees', 'category', 'city', 'state', 'funded_date',
'raised_amount', 'raised_currency', 'round']
values = ['digg', 'Digg', '60', 'web', 'San Francisco', 'CA', '1-Dec-06', '8500000', 'USD', 'b']
for i in range(0, len(keys)):
assert row[keys[i]] == values[i]


test_where_returns_events()
test_where_returns_correct_keys()
test_where_returns_events_by_city()
test_where_returns_events_by_state()
test_where_returns_events_by_company()
test_where_returns_events_by_type()
test_where_returns_no_events()
test_find_by_event_by_company_name()
test_find_by_event_by_state()