Задания седьмой недели начального курса CS50 (Similarities)

5 (100%) 2 vote[s]

В этом задании необходимо выполнить similarities (сравнение текстов) и создать свою страницу-опросник (survey).

Python поставляется со многими функциями и библиотеками. Сегодня мы будем использовать Python для генерации HTML для веб-страниц и посмотрим, как можно реализовать разделение задач.

Веб-запросы в HTTP могут выглядеть следующим образом:

GET / HTTP/1.1 Host: www.example.com ...

Ответ сервера будет примерно таким:

HTTP/1.1 200 OK Content-Type: text/html ...

...фактический HTML страницы.

Flask (Колба)

  • В этом задании используется Flask, микрорамку или набор кода, который позволяет создавать программы без повторного или совместного написания кода. (Bootstrap, например, является основой для CSS.)
      • Flask написан на Python и представляет собой набор библиотек кода, которые можно использовать для написания веб-сервера на Python.
      • Одной из методологий организации кода веб-сервера является MVC или Model-View-Controller:
      • До сих пор все предыдущие программы курса были в категории «Контроллер», в них есть логика и алгоритмы, которые решают некоторую проблему и выводят вывод на терминал. Но в веб-программировании нужно добавить форматирование и эстетику (компонент View), а также более организованный доступ к данным (компонент Model). Когда мы начнем писать код веб-сервера на Python, большая часть логики будет в контроллерах.
      • Организовав программу таким образом, мы можем разделить задачи.

Сегодня мы создадим веб-сайт, на котором можно заполнить форму для регистрации.

Мы можем начать с открытия CS50 IDE и написать некоторый код Python, который представляет собой простую программу веб-сервера serve.py:

from http.server import BaseHTTPRequestHandler, HTTPServer
 
class HTTPServer_RequestHandler(BaseHTTPRequestHandler):
 
    def do_GET(self):
 
        self.send_response(200)
 
        self.send_header("Content-type", "text/html")
        self.end_headers()
 
        self.wfile.write(b"<!DOCTYPE html>")
        self.wfile.write(b"<html lang='en'>")
        self.wfile.write(b"<head>")
        self.wfile.write(b"<title>hello, title</title>")
        self.wfile.write(b"</head>")
        self.wfile.write(b"<body>")
        self.wfile.write(b"hello, body")
        self.wfile.write(b"</body>")
        self.wfile.write(b"</html>")
 
 
port = 8080
server_address = ("0.0.0.0", port)
httpd = HTTPServer(server_address, HTTPServer_RequestHandler)
 
httpd.serve_forever() 
      • Мы уже знаем, как написать привет, всемирную HTML-страницу, но сейчас мы пишем программу на Python, которая фактически генерирует и возвращает HTML-страницу.
      • Большая часть этого кода основана на httpбиблиотеке, которую мы можем импортировать, которая обрабатывает уровень HTTP, но мы написали нашу собственную do_GETфункцию, которая будет вызываться каждый раз, когда мы получаем запрос GET. Как обычно, нам нужно посмотреть на документацию для библиотеки, чтобы понять, что мы должны написать, и что у нас есть для нас. Сначала мы отправляем код состояния 200 и отправляем заголовок HTTP, указывающий, что это HTML-страница. Затем мы записываем (как байты ASCII) некоторый HTML построчно в ответ.
      • Обратите внимание, что мы настроили сервер на использование порта 8080 (поскольку сама среда IDE использует порт 80) и фактически создали и запустили сервер (на основе документации, которую мы нашли в Интернете).
      • Теперь, если мы запустим python serve.py, мы можем нажать CS50 IDE> Веб-сервер, чтобы открыть веб-сервер нашего IDE на другой вкладке для нас, и мы увидим привет, мировую страницу, которую мы только что написали.

Реализация многих общих функций веб-сервера может быть утомительной даже с библиотекой HTTP, поэтому такая среда, как Flask, очень помогает в предоставлении абстракций и ярлыков, которые мы можем использовать повторно.

С помощью Flask можно записать в application.pyфайл следующее:

from flask import Flask, render_template, request   app = Flask(__name__)   @app.route("/") def index():     return "hello, world"
        • С помощью app = Flask(__name__)мы инициализируем приложение Flask для нашего application.pyфайла. Затем мы используем @app.route("/")синтаксис, чтобы указать, что функция ниже будет отвечать на любые запросы /или корневую страницу нашего сайта. Мы называем эту функцию indexпо соглашению, и она просто вернет «hello, world» в качестве ответа без HTML.
        • Теперь мы можем позвонить flask run из терминала в той же папке, что и наша application.py, и в результате URL-адреса отобразится страница с надписью «привет, мир» (которую наш браузер отображает даже без HTML).

Мы можем изменить index функцию, чтобы она возвращала шаблон или файл с HTML, который мы написали, который действует как представление.

return render_template("index.html")
      • В templatesпапке у нас будет index.htmlфайл со следующим:
<!DOCTYPE html>

<html lang="en">
    <head>
        <meta name="viewport" content="initial-scale=1, width=device-width">
        <title>hello</title>
    </head>
    <body>
        hello, 
    </body>
</html>
      • Мы видим новую функцию, «, как заполнитель. Поэтому мы вернемся и изменим логику indexнашего контроллера, чтобы проверить параметры в URL и передать их представлению:
return render_template("index.html", name=request.args.get("name","world"))
      • Мы используем request.args.getдля получения параметра из URL запроса называется name. (Второй аргумент, worldбудет значением по умолчанию, которое будет возвращено, если оно не было установлено.) Теперь мы можем зайти, /?name=Davidчтобы увидеть «привет, Дэвид» на странице. Теперь мы можем создавать бесконечное количество веб-страниц, хотя мы написали всего несколько строк кода.

В froshims0, мы можем написать, application.pyчто может получить и ответить на запрос POST из формы:

from flask import Flask, render_template, request

app = Flask(__name__)


@app.route("/")
def index():
    return render_template("index.html")


@app.route("/register", methods=["POST"])
def register():
    if not request.form.get("name") or not request.form.get("dorm"):
        return render_template("failure.html")
    return render_template("success.html")
      • Для страницы по умолчанию мы вернем объект index.html, который содержит форму:
{% extends "layout.html" %}

{% block body %}
    <h1>Register for Frosh IMs</h1>
    <form action="/register" method="post">
        <input autocomplete="off" autofocus name="name" placeholder="Name" type="text">
        <select name="dorm">
            <option disabled selected value="">Dorm</option>
            <option value="Apley Court">Apley Court</option>
            <option value="Canaday">Canaday</option>
            <option value="Grays">Grays</option>
            <option value="Greenough">Greenough</option>
            <option value="Hollis">Hollis</option>
            <option value="Holworthy">Holworthy</option>
            <option value="Hurlbut">Hurlbut</option>
            <option value="Lionel">Lionel</option>
            <option value="Matthews">Matthews</option>
            <option value="Mower">Mower</option>
            <option value="Pennypacker">Pennypacker</option>
            <option value="Stoughton">Stoughton</option>
            <option value="Straus">Straus</option>
            <option value="Thayer">Thayer</option>
            <option value="Weld">Weld</option>
            <option value="Wigglesworth">Wigglesworth</option>
        </select>
        <input type="submit" value="Register">
    </form>
{% endblock %}
      • У нас есть HTML-форма с inputтэгом для студента, чтобы ввести его имя, и selectтэг, чтобы создать выпадающий список для них, чтобы выбрать общежитие. Наша форма будет отправлена ​​по маршруту, который мы вызываем /register, и мы будем использовать метод POST для отправки информации формы.
      • Обратите внимание, что наш шаблон теперь использует новую функцию extendsдля определения блоков, которые будут заменены в другом файле layout.html:
<!DOCTYPE html>

<html lang="en">
    <head>
        <meta name="viewport" content="initial-scale=1, width=device-width">
        <title>froshims0</title>
    </head>
    <body>
        {% block body %}{% endblock %}
    </body>
</html>
      • Теперь, если у нас есть другие страницы на нашем сайте, они могут легко поделиться общей разметкой, которую мы хотели бы видеть на каждой странице. {% block body %}{% endblock %}Синтаксис является заполнителем блок в термос, где другие страницы, как index.html, может предоставить HTML, которые будут подставлены в этом блоке.
    • В нашей registerфункции мы укажем, что мы слушаем запрос POST, а внутри функции просто убедитесь, что мы получили значение для обоих nameи dormrequest.formэто абстракция, предоставляемая Flask, так что мы можем получить доступ к аргументам или параметрам из данных POST запроса.

Когда мы запускаем наше приложение flask runи посещаем URL, иногда мы можем увидеть внутреннюю ошибку сервера. И если мы вернемся к нашему терминалу, где работает наш сервер Flask, мы увидим сообщение об ошибке, которое даст нам подсказку о том, что пошло не так. Мы можем нажать Control + C, чтобы остановить наш веб-сервер, внести изменения, которые, как мы надеемся, исправят нашу ошибку, и снова запустить наш веб-сервер. И даже если ничего не сломано, но мы внесли изменение, иногда нам нужно выйти из Flask и запустить его заново, чтобы он заметил эти изменения.

Нам также нужен каталог success.htmlи failure.htmlв нашем templatesкаталоге, который может выглядеть так:

{% extends "layout.html" %}
 {% block body %}
     You are registered! (Well, not really.)
 {% endblock %}
    • Наша registerфункция вернет это с полностью обработанным шаблоном, если мы предоставили имя и общежитие в форме.
    • При этом layout.htmlнам не нужно было копировать и вставлять одну <head>и ту же и другую общую разметку, чтобы нам было легче вносить изменения на всех страницах, которые у нас есть одновременно.

Страница сбоя также будет иметь тот же макет, но отправит другое сообщение:

{% %}Синтаксис на самом деле называется дзиндзя, языка шаблонного,  что колба способна понять и положить вместе.

И весь этот код Python живет на нашем сервере в IDE CS50, каждый раз генерируя заполненную HTML-страницу и отправляя ее в браузер в ответ. Это можно увидеть, щелкнув правой кнопкой мыши страницу в Chrome, выбрав «Просмотреть исходный код» и увидев полный HTML-код, который получат пользователи.

Теперь давайте на самом деле сделаем что-то с отправленной информацией формы. В froshims1/application.py, мы создадим список для хранения всех зарегистрированных студентов:

from flask import Flask, redirect, render_template, request

# Configure app
app = Flask(__name__)

# Registered students
students = []


@app.route("/")
def index():
    return render_template("index.html")


@app.route("/registrants")
def registrants():
    return render_template("registered.html", students=students)


@app.route("/register", methods=["POST"])
def register():
    name = request.form.get("name")
    dorm = request.form.get("dorm")
    if not name or not dorm:
        return render_template("failure.html")
    students.append(f"{name} from {dorm}")
    return redirect("/registrants")

Мы создаем пустой список, students = []и когда мы получим имя и общежитие register, мы будем использовать students.append(f"{name} from {dorm}")для добавления в список форматированную строку с этим именем и общежитием students.

В registrantsфункции мы передадим в нашем studentsсписке шаблон registered.html:

{% extends "layout.html" %}   {% block body %}     <ul>         {% for student in students %}             <li>{{ student }}</li>         {% endfor %}     </ul> {% endblock %}
    • Обратите внимание, что с Jinja у нас могут быть простые концепции, такие как forцикл для генерации HTML на основе переменных, переданных в шаблон. (Нам нужно, endforпоскольку в HTML отступы нужны только для стилистических целей, поэтому нам нужно указать, когда заканчивается цикл.) Здесь мы создаем <li>для каждого studentили строки studentsпеременную, которая была передана контроллер, application.py. И обратите внимание, что разметка или форматирование списка находятся в этом шаблоне или представлении.

Если мы остановим наш сервер и перезапустим его, мы потеряем все данные, которые мы собрали, так как studentsпеременная создается и хранится только до тех пор, пока работает наша программа.

В froshims2/application.py, мы используем новую библиотеку:

import os
import smtplib
from flask import Flask, render_template, request

# Configure app
app = Flask(__name__)


@app.route("/")
def index():
    return render_template("index.html")


@app.route("/register", methods=["POST"])
def register():
    name = request.form.get("name")
    email = request.form.get("email")
    dorm = request.form.get("dorm")
    if not name or not email or not dorm:
        return render_template("failure.html")
    message = "You are registered!"
    server = smtplib.SMTP("smtp.gmail.com", 587)
    server.starttls()
    server.login("jharvard@cs50.net", os.getenv("PASSWORD"))
    server.sendmail("jharvard@cs50.net", email, message)
    return render_template("success.html")
    • Библиотека SMTP (Simple Mail Transfer Protocol) позволяет нам использовать абстракции для отправки электронной почты, и здесь каждый раз, когда мы получаем действительную форму, мы отправляем электронное письмо. Прочитав документацию для smtplibи для Gmail, мы можем определить строки кода, необходимые для программного входа на сервер Gmail, и отправить электронное письмо на адрес электронной почты из нашей формы.

Мы также можем сохранить регистрационные данные в CSV на нашем сервере, который затем может быть открыт даже после остановки нашего сервера:

from flask import Flask, render_template, request
import csv

app = Flask(__name__)


@app.route("/")
def index():
    return render_template("index.html")


@app.route("/register", methods=["POST"])
def register():
    if not request.form.get("name") or not request.form.get("dorm"):
        return render_template("failure.html")
    file = open("registered.csv", "a")
    writer = csv.writer(file)
    writer.writerow((request.form.get("name"), request.form.get("dorm")))
    file.close()
    return render_template("success.html")


@app.route("/registered")
def registered():
    file = open("registered.csv", "r")
    reader = csv.reader(file)
    students = list(reader)
    return render_template("registered.html", students=students)

Мы импортируем csvбиблиотеку и открываем файл, который registered.csvнужно добавить или прочитать. Если мы получили форму в registerмаршруте, мы откроем файл a, чтобы добавить. Затем мы создаем csv.writer(на основе документации для библиотеки) и используем writerowфункцию для записи имени и общежития в файл. Наконец, мы закроем файл.

registeredМаршрут будет открыть файл для чтения, а также создать список списков на основе файла. Затем registered.htmlмы можем выполнить итерацию по каждому списку в списке (каждой строке) и напечатать первый элемент (имя) и второй элемент (общежитие):

{% extends "layout.html" %}

{% block body %}
    <h1>Registered</h1>
    <ul>
        {% for student in students %}
            <li>{{ student[0] }} from {{ student[1] }}</li>
        {% endfor %}
    </ul>
{% endblock %}

С языком, который мы рассмотрим на следующей неделе, SQL, мы сможем работать с данными проще, чем с CSV-файлом.

В froshims6/templates/index.htmlнашем шаблоне мы используем JavaScript для немедленной проверки ввода:

{% extends "layout.html" %}

{% block body %}
    <h1>Register for Frosh IMs</h1>
    <form action="/register" method="post">
        <input autocomplete="off" autofocus name="name" placeholder="Name" type="text">
        <select name="dorm">
            <option disabled selected value="">Dorm</option>
            <option value="Apley Court">Apley Court</option>
            <option value="Canaday">Canaday</option>
            <option value="Grays">Grays</option>
            <option value="Greenough">Greenough</option>
            <option value="Hollis">Hollis</option>
            <option value="Holworthy">Holworthy</option>
            <option value="Hurlbut">Hurlbut</option>
            <option value="Lionel">Lionel</option>
            <option value="Matthews">Matthews</option>
            <option value="Mower">Mower</option>
            <option value="Pennypacker">Pennypacker</option>
            <option value="Stoughton">Stoughton</option>
            <option value="Straus">Straus</option>
            <option value="Thayer">Thayer</option>
            <option value="Weld">Weld</option>
            <option value="Wigglesworth">Wigglesworth</option>
        </select>
        <input type="submit" value="Register">
    </form>

    <script>

        document.querySelector('form').onsubmit = function() {
            if (!document.querySelector('input').value) {
                alert('You must provide your name!');
                return false;
            }
            else if (!document.querySelector('select').value) {
                alert('You must provide your dorm!');
                return false;
            }
            return true;
        };

    </script>

{% endblock %}
  
С помощью JavaScript на странице пользователь может сразу же 

С помощью JavaScript на странице пользователь может сразу же получить отзыв, поскольку он запускается в браузере. И мы все равно должны проверить входные данные на нашем сервере, так как кто-то может отключить JavaScript или попытаться отправить неверные запросы программно. С такими библиотеками, как Bootstrap, мы можем сделать валидацию приятной и действительно улучшить пользовательский интерфейс или UX.

В этом примере у нас есть функция, которая будет вызываться при отправке formна страницу, и проверяет, есть ли значение как для, так inputи для select. Если для одного из них нет значения, мы создадим оповещение и return fallseостановим отправку формы. В противном случае наша функция будет работать, return trueесли они оба присутствуют, что позволяет отправлять форму браузеру.

Мы также можем выделить код JavaScript в .jsфайл и включить его, но, поскольку у нас пока нет очень большого количества строк кода, мы можем принять проектное решение, чтобы включить наш код JavaScript непосредственно в наш шаблон. Фреймворки, такие как React, будут организовывать код представления, например HTML и JavaScript, особым образом, чтобы мы могли поддерживать согласованные шаблоны в более сложных веб-приложениях.

Слова

Создадим веб-сайт, на котором кто-то сможет искать слова, начинающиеся с какой-то строки, очень похоже на то, как мы хотим получать автозаполнение. Нам понадобится файл, который называется largeсписком словарных слов, и  words0/application.py будет таким:

from flask import Flask, render_template, request

app = Flask(__name__)

WORDS = []
with open("large", "r") as file:
    for line in file.readlines():
        WORDS.append(line.rstrip())

@app.route("/")
def index():
    return render_template("index.html")


@app.route("/search")
def search():
    words = [word for word in WORDS if word.startswith(request.args.get("q"))]
    return render_template("search.html", words=words)

Когда сервер запустится, создадим WORDSсписок из чтения в каждой строке large файла, удалив новую строку с rstrip и сохранив ее в нашем списке.

В indexфункции будем рендерить index.html, это просто форма:

{% extends "layout.html" %}

{% block body %}
    <form action="/search" method="get">
        <input autocomplete="off" autofocus name="q" placeholder="Query" type="text">
        <input type="submit" value="Search">
    </form>
{% endblock %}
      • Форма будет использовать getметод, так как мы хотим, чтобы запрос был в URL.
      • На searchмаршруте мы создаем список words, который представляет собой список каждого word в нашем глобальном WORDSсписке (который мы читали ранее), который начинается со значения параметра q. Это эквивалентно:
words = []
q = request.args.get("q")
for word in WORDS:
    if word.startswith(q):
        words.append(word)
      • Как только у нас будет список слов, которые соответствуют, мы передадим его в наш шаблон, search.htmlкоторый будет отображать каждое из них с разметкой.
      • Мы можем запустить наш сервер с flask run, и когда мы посещаем URL, мы видим форму, в которую мы можем ввести некоторые данные. Если мы введем букву a или b, мы можем нажать «Отправить» и перейти на страницу со всеми словами в нашем словаре, которые начинаются с a или b. И мы замечаем, что наш маршрут примерно такой /search?q=a, хотя мы могли бы изменить q (для запроса) на что угодно. Мы даже можем изменить URL-адрес с другим значением для q, и увидеть наши результаты отображаются.

Через words1, мы сразу получим список результатов с JavaScript. И мы можем сделать вывод, как работает этот пример, прежде чем смотреть на код, запустив его в IDE. Мы можем посетить URL-адрес и использовать вкладку «Сеть» в Инструментах разработчика, щелкнув правой кнопкой мыши страницу в Chrome: 

    • Мы видим, что наш браузер делает запрос каждый раз, когда мы вводим в поле ввода, и если мы щелкаем по запросу, а затем по Response, мы видим, что наш браузер получил некоторый фрагмент HTML с нашими результатами.

Мы можем нажать View Source на странице и увидеть, что на нашей странице есть немного JavaScript после HTML:

<input autocomplete="off" autofocus placeholder="Query" type="text">

<ul></ul>

<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>

    let input = document.querySelector('input');
    input.onkeyup = function() {
        $.get('/search?q=' + input.value, function(data) {
            document.querySelector('ul').innerHTML = data;
        });
    };

</script>
    • Здесь мы используем библиотеку JavaScript с именем jQuery, которая предоставляет нам некоторые абстракции. Мы выбираем inputэлемент, и каждый раз, когда keyupпроисходит событие, мы хотим изменить страницу. keyupСобытие произойдет , когда мы нажмем клавишу в поле ввода, и отпустить. Мы используем $.getфункцию jQuery, чтобы сделать запрос GET к нашему серверу на /search?q=маршруте с добавленным значением поля ввода. Когда мы dataвернемся назад, $.getфункция вызовет анонимную функцию (обратный вызов), чтобы установить innerHTMLдля этого ulна нашей странице значение data.
    • И обратите внимание, что мы предоставили пустой открытый и закрытый <ul>элемент в нашем шаблоне, но мы изменим HTML внутри того, что отвечает наш сервер.

В нашем серверном коде наш searchмаршрут в основном такой же, как и раньше, но шаблон search.htmlбудет содержать только <li>элементы, по одному для каждого соответствующего слова:

{% for word in words %}     <li>{{ word }}</li> {% endfor %}
    • Поскольку мы не расширяем a layout.html, этот маршрут будет возвращать только неполный фрагмент HTML. Но это все еще работает, потому что наш код JavaScript помещает его в полную страницу, нашу index.html.

Благодаря words2нашему серверу мы можем более эффективно возвращать данные в формате JSON, JavaScript: 

    • Затем в нашем коде JavaScript на странице мы напишем каждый из них как <li>, генерируя разметку в браузере, а не на нашем сервере.
    • Код Python application.pyиспользует jsonifyфункцию для возврата списка в виде объекта JSON:
@app.route("/search")
def search():     q = request.args.get("q")     words = [word for word in WORDS if q and word.startswith(q)]     return jsonify(words)
    • В index.html при помощи JavaScript добавим каждое слово как <li>элемент:
let input = document.querySelector('input'); input.onkeyup = function() {     $.get('/search?q=' + input.value, function(data) {         let html = '';         for (word of data) {             html += '<li>' + word + '</li>';         }         document.querySelector('ul').innerHTML = html;     }); };

Поскольку браузер может запускать JavaScript, который может выполнять поиск в списке, мы можем написать все это в JavaScript, не отправляя запрос на сервер:

let input = document.querySelector('input');
input.onkeyup = function() {
    let html = '';
    if (input.value) {
        for (word of WORDS) {
            if (word.startsWith(input.value)) {
                html += '<li>' + word + '</li>';
            }
        }
    }
    document.querySelector('ul').innerHTML = html;
};
    • Когда мы получим ввод от пользователя, мы просто переберем WORDSмассив и добавим любую wordстроку, которая начинается со значения ввода, как страницу <li>.
    • Мы также должны включить large.jsфайл, который создает эту глобальную переменную WORDS, которая начинается со следующего:
let WORDS = [   "a",   "aaa",   "aaas",   "aachen",   "aalborg",   "aalesund",   "aardvark",   ...

Даже на относительно простом примере мы видим, как может быть несколько разных подходов к решению одной и той же проблемы. С версией 0 наш сервер отправлял обратно все полные страницы по каждому запросу. В версии 1 мы использовали JavaScript для отправки запросов без перехода на другую страницу, получения данных с разметкой с сервера. В версии 2 мы использовали JavaScript, но получили только те данные с сервера, которые мы затем разметили в браузере.

Наконец, в версии 3 мы использовали JavaScript и список слов для достижения тех же результатов, но все в браузере. У каждого подхода есть свои плюсы и минусы, поэтому в зависимости от того, какие компромиссы мы ценим, одно решение может быть лучше, чем остальные.

На основе оригинальных материалов заметок к лекции 7 недели курса HarvardX: CS50CS50 — введение в информатику

Similarities tl;dr (легкий)

      1. Реализуйте программу, которая сравнивает два файла по сходству.
      2. Реализуйте веб-сайт, который подчеркивает сходство между файлами, как показано ниже.

Введение

Определение того, являются ли два файла идентичными, является (относительно!) тривиальным: итерируйте по символам в каждом, проверяя, идентичны ли каждый из них. Но определить, похожи ли два файла, нетривиально. В конце концов, что значит быть похожим? Возможно, файлы имеют общие строки. Возможно, файлы имеют общие предложения. Возможно, файлы имеют только общие подстроки.

Достаточно сказать, что задача состоит в том, чтобы определить, похожи ли два файла!

Прежде, чем начать

Нужно загрузить материалы задачи к вашей среды CS50 IDE. Войдите в CS50 IDE, а затем в окне терминала выполните команды, приведенные далее.

    1. Выполните update50 чтобы убедиться, что ваше среда IDE обновлено. Во исполнение этой команды может уйти несколько минут.
    2. Выполните cd чтобы убедиться, что вы находитесь в ~ / workspace / (то есть в папке под названием workspace, которая находится в вашем домашнем каталоге ~).
    3. Выполните mkdir pset7, чтобы создать папку под названием pset7 в вашей папке workspace, если такой папки у вас еще нет.
    4. Выполните cd pset7 для перехода к (открытие) этой папки.
    5. Выполните wget http://cdn.cs50.net/2018/fall/psets/7/similarities/similarities.zip для загрузки заархивированного ZIP-файла с исходными материалами задачи.
    6. Выполните unzip similarities.zip для разархивирования файла.
    7. Выполните rm similarities.zip и укажите yes или y чтобы удалить ZIP-файл.
    8. Выполните ls. Вы увидите папку similarities, которая находилась внутри ZIP-файла.
    9. Выполните cd similarities, чтобы перейти в эту папку.
    10. Выполните ls. Вы увидите материалы задачи внутри папки.

$ cd
$ Mkdir pset7
$ Cd pset7
$ Wget http://cdn.cs50.net/2018/fall/psets/7/similarities/similarities.zip
$ Unzip similarities.zip
$ Rm similarities.zip
$ Cd similarities
$ Chmod a + x compare
$ ls
application.py compare * helpers.py inputs / requirements.txt static / templates /
понимание
inputs /
Внутри этой папки куча входных данных для примера, которые вы можете сравнивать, в частности, для поиска сходств!

compare

Откройте файл compare. Легко увидеть, что имя файла не заканчивается на .py, хотя файл содержит программу, написанную на Python. Но это нормально! Обратите внимание на «шебанг» (символ #!) Сверху файла:

! / Usr / bin / env python3

Эта строка говорит компьютеру интерпретировать (то есть выполнять) эту программу с использованием python3 (он же — python в CS50 IDE), интерпретатора, что понимает Python 3.

Обратите внимание, как этот файл определяет функцию main и вызывает в нижней части файла. Определять main не является обязательным в Python, но определение функции до того, как вы будете ее вызвать, является обязательным. Соответственно, поскольку main вызывает функцию под названием positive, и потому, что мы хотели держать главную часть этой программы в верхней части файла, был смысл реализовать main как функцию. Таким образом, main впервые вызывается в нижней части файла (после того, как было определено positive), несмотря на то, что main реализовано в верхней части файла.

Нет нужды понимать каждую строку кода в compare, но обратите внимание, согласно комментариям, что в целом делает этот код: он «парсить» предоставлены аргументы командной строки, считывает два файла в переменные как строки, сравнивает эти две строки и затем выводит список сходств. Строки сравниваются между собой одним из трех способов, определяется аргументом командной строки: строка к строке, предложения к предложению или подстрока к подстроки.

helpers.py

Откройте файл helpers.py. Ох, знакомый нам TODO. В этом файле определены три функции, каждая из которых должна реализовывать разный алгоритм сравнения: для строк, предложений и подстрок. Сейчас каждая из них возвращает пустой список. Но это не надолго!

application.py

Откройте файл application.py. Этот файл реализует веб-приложение, которое, в конце концов, позволит вам выполнять любой из этих трех алгоритмов на любых двух текстовых файлах. Нет нужды полностью понимать этот файл, особенно highlight и errorhandler. Но знайте, что highlight, при условии, что ему предоставили строку s и список других строк strings, подсвечивает (путем обращения их в HTML-тег span) все вхождения этой строки в список. А errorhandler обеспечивает то, что любые HTTP-ошибки будут отображены на собственной странице.

Обязательно прочитайте функции index и compare, последняя из которых обрабатывает отправки формы.

templates / layout.html

Откройте файл templates / layout.html. В этом файле находится шаблон общего вида для веб-приложения. Наверное, вы узнаете некоторые HTML-теги и увидите новые. Обратите внимание, как шаблон использует популярную библиотеку Bootstrap. На самом деле, мы взяли за основу шаблон из этой библиотеки starter template.

templates / index.html

Откройте файл templates / index.html. Ох, последнее TODO. Обратите внимание, как этот шаблон «расширяет» (extends) layout.html, что говорит нам о том, что layout.html является основой, из которой будет сделано index.html. block, определенный в index.html будет вставлен в соответствующее место для block в layout.html.

В конце концов, этот файл будет содержать форму, с помощью которой пользователь сможет загрузить два файлы к вашему веб-приложения для сравнения одним из ваших трех алгоритмов

templates / compare.html

Откройте файл templates / compare.html. Мы реализовали этот файл для вас. Благодаря его использованию CSS (особенно класса под названием col-6), он обеспечивает, что файлы пользователя, как только они будут загружены и подсвеченные, будут представлены друг к другу.

templates / error.html

Откройте файл templates / error.html. В этом файле находится шаблон, с помощью которого будут показаны все HTTP ошибки. Для этого он использует компонент Bootstrap под названием Jumbotron.

static / styles.css

Откройте файл static / styles.css. В этом файле находятся некоторые CSS свойства, которые вместе реализуют интерфейс вашего веб-приложения. На самом деле, они модифицируют некоторые значения Bootstrap по умолчанию.

requirements.txt

Откройте файл requirements.txt (не меняя его, хотя в будущем вы можете это сделать, если захотите). Этот файл определяет библиотеки, по одной в строку, от которых зависит вся эта функциональность.

Спецификация

helpers.py
lines

Реализуйте функцию lines таким образом, что если ей передать две строчные переменные (string), a и b, она вернет список list строк, которые присутствуют и в a и в b. В списке не должно быть дубликатов. Считайте, что строки в a и b будут разделены символом \ n, но строки в списке list возвращаемого не должны заканчиваться на \ n. Если и a, и b содержат один или более пустых строк (то есть \ n перед которым нет никаких других символов), список list, что будут возвращены, должен содержать пустую строку (то есть »).

sentences

Реализуйте функцию sentences таким образом, что если ей передать две строчные переменные (string), a и b, она возвращает список list уникальных предложений на английском языке, которые присутствуют и в a, и в b. В списке list не должно быть дубликатов. Используйте sent_tokenize с Natural Language Toolkit для того, чтобы разбить каждую строку на список предложений ( «токенизуваты»). Его можно импортировать с помощью:

from nltk.tokenize import sent_tokenize

Согласно его документации, sent_tokenize, при условии, что ему предоставили str как входные данные, возвращает список предложений на английском языке, присутствуют в этой строке. Эта функция считает, что ее входные данные на самом деле является текстом на английском языке (а не, например, кодом, который случайно также может содержать точки).

substrings

Реализуйте функцию substrings таким образом, что если ей передать две строчные переменные (string), a и b, и целое число n, она возвращает список list всех подстрок длиной n присутствующих и в a, и в b. В списке не должно быть дубликатов.

Вспомните, что подстрокой длины n некоторого строки является лишь последовательностью с n символов этой строки. Например, если n равно 2, а строкой является Yale, существует три возможных подстроки длине 2: Ya, al, le. В то же время, если n равен 1, а строкой является Harvard, существует семь возможных подстрок длины 1: H, a, r, v, a, r и d. Но после того, как мы удалим все дубликаты, существует лишь пять уникальных подстрок: H, a, r, v, and d.

templates / index.html

Реализуйте templates / index.html таким образом, чтобы в нем была HTML-форма, с помощью которой пользователь может отправлять:

      • файл с названием file1
      • файл с названием file2
      • значение lines, sentences или substrings для поля input, что называется algorithm
      • число с названием length

Вы можете смотреть на HTML с развязку команды при необходимости, но сначала попробуйте создать правильный синтаксис самостоятельно, используя https://www.google.com/search?q=html+forms! тестирование.
Для того, чтобы проверить вашу реализацию lines, sentences и / или substrings с помощью командной строки, выполните compare следующим образом, где FILE1 и FILE2 — два любые текстовые файлы (например, те, что внутри папки inputs /):

./compare —lines FILE1 FILE2
./compare —sentences FILE1 FILE2
./compare —substrings 1 FILE1 FILE2
./compare —substrings 2 FILE1 FILE2

Для того, чтобы проверить вашу реализацию через веб-приложение, выполните

flask run

и перейдите по URL, которое было выведено на экран.

Обязательно проверьте ваш вариант с файлами из папки inputs / (также доступны по ссылке: в браузере) и на своих собственных файлах!

check50
check50 cs50 / 2018 / fall / similarities
style50
style50 helpers.py

Решение similarities команды курса
CLI
~ Cs50 / pset6 / less / compare
Web
http://similarities.cs50.net/less

Реализация index.html

Вот пример, как реализовать в similarities файл index.html:

{% extends "layout.html" %}

{% block body %}

    <div class="col">
        <form action="/compare" enctype="multipart/form-data" method="post">

            <div class="form-group">
                <input class="form-control-file" name="file1" type="file">
            </div>
            <div class="form-group">
                <input class="form-control-file" name="file2" type="file">
            </div>
            <div class="form-group">
                <div class="form-check">
                    <label class="form-check-label">
                        <input class="form-check-input" name="algorithm" type="radio" value="lines">
                        Compare lines
                    </label>
                </div>
                <div class="form-check">
                    <label class="form-check-label">
                        <input class="form-check-input" name="algorithm" type="radio" value="sentences">
                        Compare sentences
                    </label>
                </div>
                <div class="form-check">
                    <label class="form-check-label">
                        <input class="form-check-input" name="algorithm" type="radio" value="substrings">
                        Compare substrings
                        <input class="form-control form-control-sm" min="1" name="length" placeholder="of length n" type="number">
                    </label>
                </div>
            </div>
            <button class="btn btn-default" type="submit">Compare</button>

        </form>
    </div>

{% endblock %}

Реализация helpers.py

Пример реализации в similarities helpers.py:

from nltk.tokenize import sent_tokenize


def lines(a, b):
    """Return lines in both a and b"""

    a_lines = set(a.split("\n"))
    b_lines = set(b.split("\n"))

    return a_lines & b_lines


def sentences(a, b):
    """Return sentences in both a and b"""

    a_sentences = set(sent_tokenize(a))
    b_sentences = set(sent_tokenize(b))

    return a_sentences & b_sentences


def substring_tokenize(str, n):
    substrings = []

    for i in range(len(str) - n + 1):
        substrings.append(str[i:i + n])

    return substrings


def substrings(a, b, n):
    """Return substrings of length n in both a and b"""

    a_substrings = set(substring_tokenize(a, n))
    b_substrings = set(substring_tokenize(b, n))

    return a_substrings & b_substrings

Для проверки similarities онлайн отсылается только этот файл. Для сдачи задания на eDX (для получения сертификата от
HarvardX: CS50CS50) нужно загружать весь проект в репозиторий GitHub.

Видео проверки работы приложения similarities:

В помощь в работе над Survey

Для работы над опросником, можно воспользоваться шаблоном со встроенным ренднрингом кода. Это ускорит работу.

Читайте больше по теме:

Подписаться
Уведомление о
guest
0 комментариев
Inline Feedbacks
View all comments
Просмотры: 1338

Популярные записи