Создайте свою собственную библиотеку ссылок

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

Утром я обычно просматриваю Hacker News, свой Medium Daily Digest, свою ленту в Twitter и, возможно, некоторые информационные бюллетени Substack. Я прочитаю некоторые статьи и добавлю в закладки много статей, чтобы прочитать их позже. Раньше я использовал закладки браузера, но это быстро вышло из-под контроля.

Мое самое последнее решение состояло в том, чтобы использовать расширение браузера Obsidian Clipper, которое сохраняет статьи в ежедневную заметку в моем хранилище Obsidian Vault. Это хорошо работает, так как я могу пометить их темой и найти их позже. Моя проблема с этим заключается в том, что трудно увидеть сводку статей и отличить прочитанное от непрочитанного. Это то, что побудило меня реализовать свое решение.

В этой статье предполагается, что читатель немного знаком с Python, AWS и SQL, и будет объяснено, как сохранять закладки URL в вашей базе данных с помощью букмарклета. В частности, реализация использует Flask, AWS Elastic Beanstalk и MySQL.

Первый шаг — развертывание приложения Flask на Beanstalk. Документация AWS довольно хороша, если у вас ее еще нет. Имейте в виду, однако, что установка Elastic Beanstalk CLI может быть немного сложной в зависимости от того, какой тип машины вы используете.

Когда у вас есть работающее приложение и вы изменили основные маршруты и шаблоны в соответствии со своими потребностями, вам нужно настроить базу данных. Первый шаг здесь — перейти на панель инструментов Elastic Beanstalk и найти среду, в которой работает ваше приложение Flask. На боковой панели щелкните Конфигурация. Прокрутите вниз, где написано «База данных».

Чтобы убедиться, что вы (скорее всего) остаетесь в рамках уровня бесплатного пользования AWS-RDS, выберите MySQL на db.t2.micro инстансе с низкой доступностью. На следующем шаге вам понадобится конечная точка базы данных, имя пользователя и пароль. Так что обратите внимание на это. Кроме того, пока вы здесь, вы должны зайти в группу безопасности и разрешить входящий трафик с вашего IP, чтобы вы могли подключаться к базе данных при локальном запуске сервера.

Теперь мы создадим базу данных и таблицу, которая будет использоваться для хранения ваших закладок. Вам нужно будет установить MySQL на свой локальный компьютер. Для меня это было сделано с.

brew install mysql-client 
export PATH=”/usr/local/opt/mysql-client/bin:$PATH”
brew install mysql

Как только это будет сделано, вы можете подключиться к своей конечной точке и создать базу данных и таблицу. После первой команды вам будет предложено ввести пароль.

mysql -h <YOUR-ENDPOINT> -u <USERNAME> -p
CREATE DATABASE MyDB;
USE MyDB;
CREATE TABLE bookmarks (id INT NOT NULL PRIMARY KEY, url VARCHAR(64), category VARCHAR(40), is_read BOOL, created_date DATETIME);

Теперь мы готовы начать работу в IDE!

Самое первое, что нужно сделать здесь, это последний шаг в настройке вашей базы данных. В том же каталоге, что и application.py, и каталог .elasticbeanstalk создайте новый каталог с именем .ebextensions. Чтобы MySQL работал на вашем экземпляре эластичных вычислений, создайте новый файл с именем 01_packages.config и добавьте следующее:

packages:
 yum:
 python3-devel: []
 mariadb-devel: []

Хорошо, теперь мы можем начать использовать Python. Активируйте виртуальную среду и

pip install flask-mysqldb

В application.py вам понадобится что-то вроде этого:

from flask_mysqldb import MySQL
# Create Flask Application
application = Flask(__name__)
application.secret_key = dotenv_values(‘.env’)[‘APP_SECRET’]

# initialize the db
application.config['MYSQL_HOST'] = dotenv_values('.env')['DB_ENDPOINT']
application.config['MYSQL_USER'] = dotenv_values('.env')['DB_USERNAME']
application.config['MYSQL_PASSWORD'] = dotenv_values('.env')['DB_PASSWORD']
application.config['MYSQL_DB'] = 'MyDB'

db = MySQL(application)

import source.routes

Мне нравится держать routes.py в чистоте, насколько это возможно, поэтому давайте начнем с bookmarks.py, где мы определим класс Bookmark и некоторые функции для управления вашими закладками.

Класс Bookmark принимает URL-адрес и категорию при инициализации, назначает текущую дату и время для created_date и предполагает, что вы еще не читали статью. В настоящее время я не использую метод категорий. Я планирую поиграть с некоторыми моделями машинного обучения, чтобы увидеть, насколько хорошо они могут помечать или классифицировать статьи, которые я им подаю.

Класс Bookmark также имеет метод для вставки объекта в базу данных. Когда вы просматриваете закладки на своем сайте, там будет флажок.

Если вы установите флажок и отправите форму, функция update_read_bookmarks() обновит базу данных.

Функция get_all_unread_bookmarks() извлекает данные, которые мы хотим отобразить в bookmarks.html.

from application import db
import datetime

class Bookmark():

  def __init__(self, url, category) -> None:
   self.url = url
   self.category = category
   self.created_date = datetime.datetime.now()
   self.is_read = False

  def insert(self):
   query = f"""INSERT INTO bookmarks (url, category, created_date, is_read)
                   VALUES ('{self.url}', '{self.category}', '{self.created_date}', {self.is_read});"""
   cursor = db.connection.cursor()
   cursor.execute(query)
   db.connection.commit()
   cursor.close()

def update_read_bookmarks(bookmarks_to_update):
  query = f"""UPDATE bookmarks
                 SET is_read = TRUE
                 WHERE id IN ({bookmarks_to_update});"""
   cursor = db.connection.cursor()
   cursor.execute(query)
   db.connection.commit()
   cursor.close()

def get_all_unread_bookmarks():
 query = """SELECT * FROM bookmarks
                WHERE is_read = False
                ORDER BY created_date DESC;"""
 cursor = db.connection.cursor()
 cursor.execute(query)
 db.connection.commit()
 bookmarks = cursor.fetchall()
 cursor.close()
 return bookmarks

Хорошо, теперь все готово для создания маршрутов и запуска!

В routes.py у вас будет что-то вроде того, что показано ниже. Маршрут закладки — это то, что используется в букмарклете, поэтому он ищет суффикс, который выглядит так: ?url=<URL TO BOOKMARK>. Когда вы используете букмарклет для добавления URL-адреса в закладки, он приведет вас к маршруту /bookmark/show, на который вы можете сослаться из index.html.

Все, что он делает, — это запрашивает базу данных о непрочитанных закладках и отображает их в таблице. Маршрут /update_bookmarks — это действие, связанное с формой в bookmarks.html, и оно будет искать поля, которые были проверены, чтобы база данных обновлялась должным образом.

from application import db
from source.bookmarks import Bookmark, get_all_unread_bookmarks, update_read_bookmarks

@application.route("/bookmark", methods=["GET", "POST"])
def bookmark():
   print(request.args)
   if token := request.args.get("token"):
       print("Found the token")
       if token == dotenv_values(".env")["BOOKMARK_TOKEN"]:
           if url := request.args.get("url"):
               bookmark = Bookmark(url=url, category="self")
               bookmark.insert()
               return redirect(url)
       else:
           print("The token was wrong")
           return welcome()
   else:
       print("Didnt find the token")
       return welcome()

   return bookmark_show()


@application.route("/bookmark/show", methods=["GET", "POST"])
@login_required
def bookmark_show():
   bookmarks = get_all_unread_bookmarks()
   return render_template("bookmarks.html", data=bookmarks)


@application.route("/update_bookmarks", methods=["GET", "POST"])
@login_required
def update_bookmarks():
   new_read_bookmarks = [str(bookmark) for bookmark in request.form]
   bookmarks_for_query = ",".join(new_read_bookmarks)

   update_read_bookmarks(bookmarks_for_query)

   bookmarks = get_all_unread_bookmarks()

   return render_template("bookmarks.html", data=bookmarks)

Я использую Flask-Login, чтобы убедиться, что я единственный, кто может взаимодействовать с базой данных. На данный момент я единственный, кто может даже просматривать закладки, но со временем я открою это, чтобы другие люди могли видеть мою «библиотеку ссылок».

Честно говоря, мне очень не хватает документации и руководств по использованию Flask-Login с MySQL, поэтому я собираюсь написать еще одну статью о том, как использовать Flask-Login с зашифрованными паролями в базе данных MySQL.

Чтобы убедиться, что я единственный, кто может добавить закладку, у меня есть токен в качестве переменной среды. У меня есть этот токен в запросе, который приходит из букмарклета; если они не совпадают, закладка не добавляется.

Последним шагом будет создание bookmarks.html. Я могу быть слишком утилитарным для своего же блага, но моя выглядит примерно так:

<html>

<body>
   <form action="/update_bookmarks" method="post">
       <input type="submit">
       <table border="1">
           <tr border="1">
               <th>URL</th>
               <th>Category</th>
               <th>Created Date</th>
               <th>Read</th>
           </tr>
           {% for item in data %}
           <br>
           <tr border="1">
               <td border="1">
                   <a href="{{item[1]}}">
                       {{item[1]}}
                   </a>
               </td>
               <td border="1">{{item[2]}}</td>
               <td border="1">{{item[4]}}</td>
               {% if item[3] %}
               <td><input type="checkbox" name="{{ item[0] }}" checked />&nbsp;</td>
               {% else %}
               <td><input type="checkbox" name="{{ item[0] }}" />&nbsp;</td>
               {% endif %}
           </tr>
           {% endfor %}
       </table>
   </form>
</body>

</html>

Последнее, что вам понадобится, это букмарклет, а именно:

javascript:location.href=’http://127.0.0.1:5000/bookmark?url='+location.href+'&token=<TOKEN>';

После того, как вы закончите тестирование, используйте свой домен вместо локального хоста, и все будет готово!

У меня большие планы на это приложение, поэтому, если вам интересно, подпишитесь на меня, чтобы вскоре прочитать о следующих шагах, или свяжитесь со мной в LinkedIn!

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