Оригинал на cs50.harvard.edu.
ПоясненияВыполнение
Финансы
Создайте веб-сайт, через который пользователи смогут «покупать» и «продавать» акции, как показано ниже.
Подготовка
Если вы не совсем понимаете, что значит покупать и продавать акции (т. е. акции компании), загляните сюда, чтобы получить руководство.
Вам надо создать C$50 Финансы — веб-приложение, с помощью которого вы сможете управлять портфелями акций. Этот инструмент не только позволит вам проверять реальные цены на акции и стоимость портфелей, но также позволит вам покупать (хорошо, «покупать») и продавать (хорошо, «продавать») акции, запрашивая цены на акции.
Действительно, существуют инструменты (один из них известен как IEX), которые позволяют загружать котировки акций через их API (интерфейс прикладного программирования), используя такие URL-адреса, как https://api.iex.cloud/v1/data/core/quote/nflx?token=API_KEY . Обратите внимание, как в этот URL-адрес встроен символ Netflix (NFLX); именно так IEX узнает, чьи данные возвращать. Эта ссылка на самом деле не вернёт никаких данных, поскольку IEX требует от вас использования ключа API, но если бы она сделала это , вы бы увидели ответ в формате JSON (нотация объектов JavaScript) следующим образом:
{
"avgTotalVolume": 15918066,
"calculationPrice": "close",
"change": -8.27,
"changePercent": -0.03074,
"close": 260.79,
"closeSource": "official",
"closeTime": 1667592000924,
"companyName": "Netflix Inc.",
"currency": "USD",
"delayedPrice": 260.81,
"delayedPriceTime": 1667591988947,
"extendedChange": 0.21,
"extendedChangePercent": 0.00081,
"extendedPrice": 261,
"extendedPriceTime": 1667606392772,
"high": 274.97,
"highSource": "15 minute delayed price",
"highTime": 1667592000831,
"iexAskPrice": None,
"iexAskSize": None,
"iexBidPrice": None,
"iexBidSize": None,
"iexClose": 260.85,
"iexCloseTime": 1667591999754,
"iexLastUpdated": None,
"iexMarketPercent": None,
"iexOpen": 271.67,
"iexOpenTime": 1667568602197,
"iexRealtimePrice": None,
"iexRealtimeSize": None,
"iexVolume": None,
"lastTradeTime": 1667591999820,
"latestPrice": 260.79,
"latestSource": "Close",
"latestTime": "November 4, 2022",
"latestUpdate": 1667592000924,
"latestVolume": 11124694,
"low": 255.32,
"lowSource": "15 minute delayed price",
"lowTime": 1667584872696,
"marketCap": 115215720136,
"oddLotDelayedPrice": 260.81,
"oddLotDelayedPriceTime": 1667591988947,
"open": 271.9,
"openTime": 1667568601785,
"openSource": "official",
"peRatio": 23.39,
"previousClose": 269.06,
"previousVolume": 7057350,
"primaryExchange": "NASDAQ",
"symbol": "NFLX",
"volume": 11124694,
"week52High": 700.99,
"week52Low": 162.71,
"ytdChange": -0.5978504176349512,
"isUSMarketOpen": False
}
Обратите внимание, что между фигурными скобками находится список пар ключ-значение,
разделенный запятыми, с двоеточием, отделяющим каждый ключ от его значения.
Мы собираемся сделать нечто очень похожее с Yahoo Finance.
Давайте теперь обратим наше внимание на получение кода, который поможет вам с этой задачей!
Начало
Войдите в cs50.dev, щелкните окно терминала и выполните cd. Вы получите приглашение в окне терминала, которое выглядит следующим образом:
$
Затем выполните
wget https://cs204.github.io/psets/data/finance.zip
чтобы загрузить ZIP-архив с именем finance.zip в свое кодовое пространство.
Затем выполните
unzip finance.zip
чтобы создать папку под названием «finance». Вам больше не нужен ZIP-файл,
поэтому вы можете выполнить
rm finance.zip
и введите «y», а затем Enter в ответ на приглашение, чтобы удалить загруженный вами ZIP-файл.
Теперь наберите
cd finance
затем нажмите Enter, чтобы перейти (т. е. открыть) этот каталог.
Ваше приглашение теперь должно выглядеть так, как показано ниже.
finance/ $
Запустите ls, и вы должны увидеть несколько файлов и папок:
app.py finance.db helpers.py requirements.txt satatic/ templates/
Если у вас возникнут какие-либо проблемы, повторите те же действия еще
раз и посмотрите, сможете ли вы определить, где вы ошиблись!
Запуск
Запустите встроенный веб-сервер Flask (в каталоге Finance/):
$ flask run
Посетите URL-адрес, выведенный flask, чтобы увидеть код дистрибутива в действии.
Однако вы пока не сможете войти или зарегистрироваться!
В каталоге finance/ выполните sqlite3 finance.db, чтобы открыть файл
finance.db с помощью sqlite3.
Запустите .schema в командной строке SQLite,
обратите внимание, что в файл finance.db входит таблица с именем users.
Взгляните на её структуру (т. е. схему).
Обратите внимание, что по умолчанию новые пользователи получат 10 000 долларов наличными.
Но если вы запустите SELECT * FROM users;, там (пока!) нет пользователей (т. е. строк) для просмотра.
Пояснения
app.py
Откройте app.py. Поверх файла находится множество импортированных файлов, в том числе модуль SQL CS50 и несколько вспомогательных функций. Подробнее о них скоро.
После настройки Flask обратите внимание, как этот файл отключает кэширование ответов (при условии, что вы находитесь в режиме отладки, который по умолчанию находится в вашем кодовом пространстве code50), чтобы вы не внесли изменения в какой-либо файл, но ваш браузер этого не заметил. Обратите внимание, как он настраивает Jinja с помощью специального «фильтра» usd — функции (определенной в helpers.py), которая упрощает форматирование значений в долларах США (USD). Затем он дополнительно настраивает Flask для хранения сеансов в локальной файловой системе (т. е. на диске), а не в файлах cookie (с цифровой подписью), что используется Flask по умолчанию. Затем файл настраивает модуль SQL CS50 для использования Finance.db. Обратите внимание, как он использует check_password_hash для сравнения хешей паролей пользователей. Также обратите внимание, как login «запоминает», что пользователь вошёл в систему, сохраняя его или её user_id, INTEGER, в session. Таким образом, любой route из этого файла может проверить, какой пользователь (если таковой имеется) вошел в систему. Наконец, обратите внимание, что после того, как пользователь успешно вошел в систему, вход будет перенаправлен на «/», и пользователь попадет на свою домашнюю страницу. Между тем, обратите внимание, что выход из системы просто очищает session, фактически выходя из системы.
Обратите внимание, как большинство маршрутов «украшены» @login_required (функция также определена в helpers.py). Этот декоратор гарантирует, что если не вошедший в систему пользователь попытается посетить любой из этих маршрутов, он или она сначала будет перенаправлен на вход в систему.
Обратите также внимание на то, что большинство маршрутов поддерживают GET и POST. Несмотря на это, большинство из них (на данный момент!) просто возвращают «извинения», поскольку они еще не реализованы.
helpers.py
Далее взгляните на helpers.py. Ах, вот реализация apology. Обратите внимание, как в конечном итоге отображается шаблон apology.html. Также внутри функции apology определяется ещё одна функция, escape, которую она использует для замены специальных символов в извинениях. Определяя escape внутри aplolgy, мы ограничили первое только вторым; никакие другие функции не смогут вызывать escape.
Далее в файле идёт login_required. Не беспокойтесь, если он выглядит загадочно, но если вы когда-нибудь задавались вопросом, как функция может возвращать другую функцию, вот пример!
После этого идёт lockup — функция, которая по символу (например, NFLX) возвращает котировку акций компании в виде словаря с тремя ключами: name, значением которого является str, название компании; price, значение которой является float; и symbol, значением которого является str, канонизированная (в верхнем регистре) версия символа акции, независимо от того, в каком регистре была буквы при передаче в lookup.
Последней в файле usd — функция, которая форматирует число с плавающей запятой в доллары США (например, 1234.56 форматируется как $1234,56).
requirements.txt
Затем взгляните на файл requirements.txt. Этот файл просто прописывает пакеты, от которых будет зависеть это приложение.
templates
Теперь посмотрите в templates/. В login.html — это, по сути, просто HTML- форма, стилизованная с помощью Bootstrap. В apology.html находится шаблон для извинения. Напомним, что извинение в helpers.py принимало два аргумента: message, которое было передано в render_template в качестве значения bottom, и, необязательного, cod, который был передан в render_template в качестве значения top. Обратите внимание в apology.html, как в конечном итоге используются эти значения! И вот почему 0:-)
Последний файл — layout.html. Он немного больше обычного, но это главным образом потому, что он оснащен модной, удобной для мобильных устройств "navbar" (панелью навигации), также основанной на Bootstrap. Обратите внимание, как он определяет блок main, внутри которого должны размещаться шаблоны (включая apology.html и login.html). Он также включает поддержку всплывающих сообщений Flask, чтобы вы могли ретранслировать сообщения с одного маршрута на другой, чтобы пользователь мог их увидеть.
Описание
register
Завершите реализацию register таким образом, чтобы пользователь мог зарегистрировать учётную запись через форму.
- Надо, чтобы пользователь вводил имя пользователя, реализованное в виде текстового поля, name которого является username. Принесите извинения, если введенные пользователем данные пусты или имя пользователя уже существует.
- Надо, чтобы пользователь ввёл пароль, реализованный в виде текстового поля с name password, а затем ещё раз тот же пароль, реализованный в виде текстового поля, name которого является confirmation. Принесите извинения, если какой-либо ввод пуст или пароли не совпадают.
- Отправьте введённые пользователем данные через POST в /register.
- ВСТАВЬТЕ нового пользователя в users, сохранив хэш пароля пользователя, а не сам пароль. Хешируйте пароль пользователя с помощью generate_password_hash. Скорее всего, вам захочется создать новый шаблон (например, register.html), очень похожий на login.html.
После того, как вы правильно реализовали регистрацию, вы сможете зарегистрировать учетную запись и войти в систему (поскольку вход и выход из системы уже работают)! И вы сможете видеть свои строки через sqlite3.
quote
Завершите реализацию quote таким образом, чтобы пользователь мог узнать текущую цену акции.
- Надо, чтобы пользователь вводил символ акции, в текстовое поле с name symbol.
- Отправьте введенные пользователем данные через POST в /quote.
- Скорее всего, вам захочется создать два новых шаблона (например, quote.html и quoted.html). Когда пользователь посещает /quote через GET, передайте страницу созданную из одного из этих шаблонов, внутри которого должна быть HTML-форма, которая отправляет в /quote через POST. В ответ на POST quote может отображать этот второй шаблон, внедряя в него одно или несколько значений из lookup.
buy
Завершите реализацию buy таким образом, чтобы пользователь мог покупать акции.
- На странице покупки создайте текстовое поле с name symbol, чтобы пользователь мог ввести символ акции. Программа должна принести извинения, если поле пустое или символ не существует (согласно возвращаемому значению поиска).
- На странице должно быть поле с name share, куда пользователь будет вводить число покупаемых акций. Программа должна принести извинения, если введённое значение являются положительным целым числом.
- Отправьте данные пользователя методом POST в маршрут /buy.
- По завершении перенаправьте пользователя на домашнюю страницу.
- Скорее всего, вам используете функцию lookup, чтобы узнать текущую цену акции.
- Скорее всего, вы используете SQL select, что бы узнать сколько денег у пользователя в настоящее время есть.
-
Добавьте в файл finance.db одну или несколько новых таблиц, с
помощью которых можно отслеживать покупки.
Храните достаточно информации, чтобы вы знали, кто, что, по какой
цене и когда купил.
- Используйте соответствующие типы данных SQLite.
- Задайте ограничение UNIQUE для всех полей, которые должны быть уникальными.
- Определите (неуникальные) индексы для всех полей, по которым вы будете выполнять поиск (например, с помощью SELECT с WHERE).
- Принесите извинения, не совершив покупку, если пользователь не может позволить себе такое количество акций по текущей цене.
- Вам не нужно беспокоиться об условиях гонки (или использовать транзакции)
После того, как вы правильно реализовали покупку, вы сможете видеть покупки пользователей в ваших новых таблицах через sqlite3.
index
Завершите реализацию index таким образом, чтобы он отображал сводную HTML-таблицу для пользователя, вошедшего в систему в данный момент, с указанием акций, которыми владеет пользователь, количества принадлежащих акций, текущей цены каждой акции и общей стоимости для каждого вида акций (т. е. количество акций умноженное на цену). Также отобразите текущий баланс денежных средств пользователя вместе с общей суммой (т. е. общей стоимостью акций плюс денежные средства).
- Скорее всего, вам захочется выполнить несколько SELECT. В зависимости от того, как вы реализуете свои таблицы, вы можете захотеть использовать GROUP BY, HAVING, SUM, WHERE.
- Скорее всего, вам захочется вызвать lookup для каждого вида акции.
sell
Завершите реализацию sell таким образом, чтобы пользователь мог продавать акции (которые ему принадлежат).
- Требуется, чтобы пользователь мог выбрать символ акции, реализованный в виде меню select, name которого — symbol. Принесите извинения, если пользователю не удалось выбрать акции или если (по какой-то причине после отправки) пользователь уже не владеет этими акциями.
- Требовать, чтобы пользователь ввёл количество акций, в текстовое поле с именем shares. Принесите извинения, если введённые не являются целым положительным числом или если пользователь не владеет таким количеством акций.
- Отправьте данные пользователя через POST в /sell.
- По завершении перенаправьте пользователя на домашнюю страницу.
- Вам не нужно беспокоиться об условиях гонки (или использовать транзакции).
history
Завершите реализацию history таким образом, чтобы она отображала HTML-таблицу, в которой суммируются все транзакции пользователя за всю историю, перечисляя построчно каждую покупку и каждую продажу.
- В каждой строке укажите, была ли акция куплена или продана, и укажите символ акции, цену (покупки или продажи), количество купленных или проданных акций, а также дату и время, когда произошла транзакция.
- Возможно, вам придётся изменить созданную вами таблицу для покупки или дополнить её дополнительной таблицей. Постарайтесь свести к минимуму дублирование.
индивидуальный подход
Внесите хотя бы один индивидуальный подход по вашему выбору:
- Разрешить пользователям менять свои пароли.
- Разрешить пользователям добавлять дополнительные деньги на свой счёт.
- Позвольте пользователям покупать больше акций или продавать акции, которыми они уже владеют, через сам индекс, без необходимости вводить символы акций вручную.
- Требовать, чтобы пароли пользователей содержали определённое количество букв, цифр и/или символов.
- Реализуйте какую-нибудь другую функцию сопоставимого масштаба.
Тестирование
Обязательно протестируйте свое веб-приложение вручную, как показано ниже.
- регистрация нового пользователя и проверка того, что страница его портфолио загружается с правильной информацией,
- запрос котировки с использованием действующего символа акции,
- покупка одной акции несколько раз, проверка правильности итоговых показателей портфеля,
- продажа всех или части акций, повторная проверка портфеля и
- проверка того, что на вашей странице истории отображаются все транзакции для вошедшего в систему пользователя.
Также проверьте некоторые неожиданные варианты использования, например
- ввод буквенных строк в формы, когда ожидаются только цифры,
- ввод нулевых или отрицательных чисел в формы, когда ожидаются только положительные числа,
- ввод значений с плавающей запятой в формы, когда ожидаются только целые числа,
- пытается потратить больше денег, чем есть у пользователя,
- пытается продать больше акций, чем есть у пользователя,
- ввод недопустимого биржевого символа и
- включая потенциально опасные символы, такие как ' и ; в SQL-запросах.
Примерное решение
Вы можете стилизовать свое приложение по-другому, но вот как выглядит вариант решения!
https://finance.cs50.net/"Не стесняйтесь зарегистрировать учётную запись и поиграть. Не используйте пароль, который вы используете на других сайтах.
Разумно посмотреть на HTML и CSS примера.
Подсказки
- Чтобы отформатировать значение как значение в долларах США (с указанием центов с точностью до двух знаков после запятой), вы можете использовать фильтр usd в своих шаблонах Jinja (печатать значения как {{ value | usd }} вместо {{ value }} .
-
В cs50.SQL есть метод execute, первый аргумент которого должен быть строкой SQL. Если эта
строка содержит параметры со знаками вопроса, к которым должны быть привязаны значения,
эти значения можно предоставить в качестве дополнительных именованных параметров для execute.
См. реализацию входа в систему для примера. Возвращаемое значение execute следующее:
- Если str является SELECT, то выполнение возвращает список из нуля или более объектов dict, внутри которого находятся ключи и значения, представляющие поля и ячейки таблицы соответственно.
- Если str представляет собой INSERT, и таблица в которую были вставлены данные, содержит автоинкрементный PRIMARY KEY, то метод execute возвращает значение первичного ключа вновь вставленной строки.
- Если строка имеет значение DELETE или UPDATE, то метод выполнения возвращает количество строк, удаленных или обновленных строкой.
- Напомним, что cs50.SQL будет регистрировать в окне вашего терминала любые запросы, которые вы выполняете с помощью метода выполнения (чтобы вы могли убедиться, что они соответствуют задуманному).
- Обязательно используйте параметры, отмеченные вопросительным знаком (т. е. стиль именованного параметра) при вызове метода execute CS50, а-ля WHERE?. Не используйте f-строки, или + (т. е. конкатенацию), чтобы защитить приложение от SQL-инъекции.
- Если (и только если) вы уже чувствуете себя комфортно с SQL, вы можете попробовать использовать SQLAlchemy Core или Flask-SQLAlchemy (т. е. SQLAlchemy ORM) вместо cs50.SQL.
- Вы можете добавить дополнительные статические файлы в static/.
- Скорее всего, вам захочется ознакомиться с документацией Jinja при реализации своих шаблонов.
- Разумно попросить других опробовать ваш сайт (и попытаться вызвать ошибки). Вы можете изменить внешний вид сайтов, например, через
- Вам может оказаться полезной документация Flask и документация Jinja!
Отправка на проверку
Разместите ваше приложение на render.com, укажите в форме адрес приложения.
- В форме укажите адрес сайта на render.com.
Свои оценки вы можете посмотреть на http://90.188.117.161:8080.