Commit 80fa93e3 authored by AntonKras's avatar AntonKras
Browse files

исправления, загрузка сразу в redis, добавление гитигнор, добавление тестов

parent 31c8de80
.idea
venv
......@@ -9,7 +9,7 @@
**Запуск**
Для запуска программы необходимо прописать
Для запуска программы необходимо прописать:
`python app.py путь к файлу формата .yml или .yaml, в котором прописаны соответствия {short_url: long_url}`
......@@ -26,3 +26,8 @@
- check_if_input_is_url - функция проверяет является ли ссылка ссылкой;
- normalize - функция нормализует url
**Документация**
Для создания необходимо ввести команду:
`python -m pydoc -w app`
......@@ -18,35 +18,34 @@
<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="os.html">os</a><br>
<a href="random.html">random</a><br>
</td><td width="25%" valign=top><a href="re.html">re</a><br>
<a href="redis.html">redis</a><br>
</td><td width="25%" valign=top><a href="string.html">string</a><br>
<a href="re.html">re</a><br>
</td><td width="25%" valign=top><a href="redis.html">redis</a><br>
<a href="sys.html">sys</a><br>
</td><td width="25%" valign=top><a href="yaml.html">yaml</a><br>
</td></tr></table></td></tr></table><p>
</td><td width="25%" valign=top><a href="uuid.html">uuid</a><br>
<a href="yaml.html">yaml</a><br>
</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#eeaa77">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl><dt><a name="-add"><strong>add</strong></a>()</dt><dd><tt>функция&nbsp;добавления&nbsp;соответствия<br>
<td width="100%"><dl><dt><a name="-add"><strong>add</strong></a>() -&gt; &lt;function redirect at 0x000000000307C268&gt;</dt><dd><tt>функция&nbsp;добавления&nbsp;соответствия&nbsp;в&nbsp;redis<br>
:return&nbsp;redirect(перенаправление&nbsp;на&nbsp;главную&nbsp;страницу):</tt></dd></dl>
<dl><dt><a name="-check_if_input_is_url"><strong>check_if_input_is_url</strong></a>(url) -&gt; True</dt><dd><tt>проверка&nbsp;ссылку&nbsp;на&nbsp;валидность<br>
:param&nbsp;ссылка:<br>
:return&nbsp;None&nbsp;or&nbsp;not&nbsp;None:</tt></dd></dl>
<dl><dt><a name="-extract_data_from_yml"><strong>extract_data_from_yml</strong></a>(urls) -&gt; dict</dt><dd><tt>извлекает&nbsp;данные&nbsp;из&nbsp;yml&nbsp;и&nbsp;записывает&nbsp;в&nbsp;redis<br>
<dl><dt><a name="-extract_yml_data_and_load_to_redis"><strong>extract_yml_data_and_load_to_redis</strong></a>(urls) -&gt; None</dt><dd><tt>извлекает&nbsp;данные&nbsp;из&nbsp;yml&nbsp;и&nbsp;загружает&nbsp;в&nbsp;redis<br>
:param&nbsp;файл&nbsp;формата&nbsp;yml&nbsp;или&nbsp;yaml:<br>
:return&nbsp;словарь&nbsp;с&nbsp;соответствиями:</tt></dd></dl>
<dl><dt><a name="-generate_short_url"><strong>generate_short_url</strong></a>() -&gt; str</dt><dd><tt>функция&nbsp;генерирования&nbsp;короткой&nbsp;ссылки&nbsp;из&nbsp;букв<br>
:return&nbsp;None</tt></dd></dl>
<dl><dt><a name="-generate_short_url"><strong>generate_short_url</strong></a>() -&gt; str</dt><dd><tt>функция&nbsp;генерирования&nbsp;уникальной&nbsp;короткой&nbsp;ссылки&nbsp;из&nbsp;букв&nbsp;и&nbsp;цифр<br>
:return&nbsp;сгенерированная&nbsp;строчка:</tt></dd></dl>
<dl><dt><a name="-main"><strong>main</strong></a>()</dt><dd><tt>функция&nbsp;рендерит&nbsp;шаблон<br>
:return&nbsp;отрендеренный&nbsp;html-файл:</tt></dd></dl>
<dl><dt><a name="-normalize"><strong>normalize</strong></a>(url: str) -&gt; str</dt><dd><tt>нормализация&nbsp;ссылки<br>
:param&nbsp;ссылка:<br>
:return&nbsp;нормализованная&nbsp;ссылка:</tt></dd></dl>
<dl><dt><a name="-redirect_somewhere"><strong>redirect_somewhere</strong></a>(path)</dt><dd><tt>функция&nbsp;перенаправляет&nbsp;пользователя&nbsp;на&nbsp;соответствующую&nbsp;короткой&nbsp;ссылке&nbsp;страницу<br>
<dl><dt><a name="-redirect_somewhere"><strong>redirect_somewhere</strong></a>(path) -&gt; &lt;function redirect at 0x000000000307C268&gt;</dt><dd><tt>функция&nbsp;перенаправляет&nbsp;пользователя&nbsp;на&nbsp;соответствующую&nbsp;короткой&nbsp;ссылке&nbsp;страницу<br>
:param&nbsp;path&nbsp;(передается&nbsp;часть&nbsp;ссылки&nbsp;после&nbsp;/):<br>
:return&nbsp;redirect&nbsp;or&nbsp;404&nbsp;Error:</tt></dd></dl>
</td></tr></table><p>
......@@ -57,5 +56,6 @@
<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><strong>app</strong> = &lt;Flask 'app'&gt;<br>
<strong>count</strong> = 0<br>
<strong>request</strong> = &lt;LocalProxy unbound&gt;</td></tr></table>
</body></html>
\ No newline at end of file
from flask import Flask, render_template, redirect, request, abort, flash
import re, os, random, string, yaml, redis, sys
from flask import Flask, render_template, redirect, request, abort, flash, url_for
import re, os, yaml, redis, sys
import uuid
app = Flask(__name__)
app.secret_key = os.urandom(24)
count = 0
@app.route('/', methods=['GET', 'POST'])
@app.route('/')
def main():
"""
функция рендерит шаблон
:return отрендеренный html-файл:
"""
yaml_dict = extract_data_from_yml(sys.argv[1])
return render_template('index.html', yaml_dict=yaml_dict)
global count
count += 1
if count == 1: # если первый вход, то читаем из yml и загружаем в redis
# auth_redis()
# r.flushall()
extract_yml_data_and_load_to_redis(sys.argv[1])
return render_template('index.html') # рендер шаблона
@app.route('/add', methods=['GET', 'POST'])
def add():
def add() -> redirect:
"""
функция добавления соответствия
функция добавления соответствия в redis
:return redirect(перенаправление на главную страницу):
"""
url = request.args.get("url")
yaml_dict = extract_data_from_yml(sys.argv[1])
url = request.form["url"]
if check_if_input_is_url(url) is None:
flash('URL введен некорректно!')
return redirect('http://127.0.0.1:5000/')
elif url in yaml_dict.values():
flash('Соответствие с данным URL уже существует!')
return redirect('http://127.0.0.1:5000/')
return abort(404)
else:
short_url = generate_short_url()
data = {short_url: url}
with open(sys.argv[1], 'a+', encoding='utf8') as f:
yaml.dump(data, f, default_flow_style=False, allow_unicode=True)
f.close()
return redirect('http://127.0.0.1:5000/')
print(data)
r.mset(data)
return redirect(url_for('main'))
@app.route('/<path:path>')
def redirect_somewhere(path):
def redirect_somewhere(path) -> redirect:
"""
функция перенаправляет пользователя на соответствующую короткой ссылке страницу
:param path (передается часть ссылки после /):
:return redirect or 404 Error:
"""
yml_dict = extract_data_from_yml(sys.argv[1])
if path in yml_dict:
return redirect(normalize(yml_dict[path]))
if r.get(path) != None:
return redirect(normalize(r.get(path)))
else:
return abort(404)
def extract_data_from_yml(urls) -> dict:
def extract_yml_data_and_load_to_redis(urls) -> None:
"""
извлекает данные из yml и записывает в redis
извлекает данные из yml и загружает в redis
:param файл формата yml или yaml:
:return словарь с соответствиями:
:return None
"""
r = redis.Redis()
final_dict = {} # словарь с данными из файла .yml
with open(urls, 'r') as stream:
......@@ -65,20 +67,19 @@ def extract_data_from_yml(urls) -> dict:
final_dict = yaml.safe_load(stream) # загрузка данных в словарь
except yaml.YAMLError as exc: # обработка исключения
print(exc)
# печать словаря в консоли
for key, value in final_dict.items():
print(key + ":" + value)
r.mset(final_dict) # загрузить в redis
return final_dict # возвращения словаря
def generate_short_url() -> str:
"""
функция генерирования короткой ссылки из букв
функция генерирования уникальной короткой ссылки из букв и цифр
:return сгенерированная строчка:
"""
rand_str = lambda n: ''.join(
[random.choice(string.ascii_lowercase) for i in range(n)]) # составление короткой ссылки
return rand_str(5)
rand_str = uuid.uuid4().hex[:5]
return rand_str
def check_if_input_is_url(url) -> None or not None:
......@@ -110,4 +111,20 @@ def normalize(url: str) -> str:
if __name__ == '__main__':
while 1:
c = int(input('Ввести логин/пароль для redis?\n1.Да\n2.Нет\n'))
if c == 1:
host = int(input('Введите хост: '))
port = int(input('\nВведите порт: '))
username = input('\nВведите логин:')
password = input('\nВведите пароль: ')
r = redis.Redis(charset="utf-8", decode_responses=True, host=host, port=port, username=username,
password=password)
break
if c == 2:
r = redis.Redis(charset="utf-8", decode_responses=True)
break
else:
print('Введена неверная команда\n')
app.run()
......@@ -4,7 +4,8 @@ ah712: yandex.ru
kzgcz: https://studfiles.net/preview/6716220/
vktlm: https://www.oslogic.ru/knowledge/598/shpargalka-po-osnovnym-komandam-postgresql/
mwimu: https://radiojazzfm.ru/
ahfsm: https://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D1%82%D0%B5%D1%80%D1%8F%D0%BD%D0%BD%D0%BE%D0%B5_%D0%BF%D0%BE%D0%BA%D0%BE%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5
ulido: https://pythonbasics.org/flask-tutorial-routes/
xcurl: https://www.imdb.com/list/ls025873906/?ref_=otl_3
nbnvo: https://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA_%D1%83%D0%BB%D0%B8%D1%86_%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D1%8B
hgfxe: https://gitlab.mai.ru/AnSKrasnov/short_url_generator/-/blob/master/templates/index.html#L9
vxytu: https://mail.yandex.ru/
......@@ -6,8 +6,8 @@
</head>
<body>
<p><b>Введите длинную ссылку:</b><br>
<form action="/add">
<input type="text" size="40" name="url">
<form action="/add" method="post">
<input type="text" size="40" name="url" required>
<button>Создать новое соответствие</button>
{% with messages = get_flashed_messages() %}
{% if messages %}
......@@ -19,10 +19,5 @@
{% endif %}
{% endwith %}
</form>
{% for key in yaml_dict %}
{{ key + ':' + yaml_dict[key]}}<br/>
{% endfor %}
</body>
</html>
\ No newline at end of file
from flask_testing import TestCase
from flask import url_for, request, redirect
import urllib.request as urllib2
from flask_testing import LiveServerTestCase
from app import app
import unittest
import sys
import flask_testing
import click
from unittest.mock import patch
from flask import request, url_for
from flask_testing import TestCase
class TestPageLoad(unittest.TestCase):
......@@ -24,26 +18,57 @@ class TestPageLoad(unittest.TestCase):
self.app = app.test_client()
self.assertEqual(app.debug, False)
def test_main_page(self):
"""
тест, проверяющий загрузку главной страницы
"""
response = self.app.get('/', follow_redirects=True)
self.assertEqual(response.status_code, 200)
def test_wrong_short_url(self):
"""
тест, проверяющий результат неправильно введенной короткой ссылки
"""
tester = app.test_client(self)
response = tester.get('/wwww', content_type='html/text')
self.assertEqual(response.status_code, 404)
def test_right_short_url(self):
"""
тест, проверяющий результат правильно введенной короткой ссылки
"""
tester = app.test_client(self)
response = tester.get('/yah44', content_type='html/text')
self.assertEqual(response.status_code, 302)
def test_post_wrong_long_url(self):
"""
тест, проверяющий результат неправильно введенной длинной ссылки (POST-запрос)
"""
tester = app.test_client(self)
response = tester.post('/add', data=dict(url='wrong_url'), follow_redirects=True)
self.assertEqual(response.status_code, 404)
def test_post_right_long_url(self):
"""
тест, проверяющий результат правильно введенной длинной ссылки (POST-запрос)
"""
tester = app.test_client(self)
response = tester.post('/add', data=dict(url='https://yandex.ru/'), follow_redirects=True)
self.assertEqual(response.status_code, 200)
# def test_redirect(self):
# tester = app.test_client(self)
# with tester:
# response = tester.get(url_for('/yah44'), follow_redirects=True)
# assert request.path == url_for('https://www.yahoo.com/')
# tester = app.test_client(self)
# with tester:
# response = tester.get(url_for('/yah44'), follow_redirects=True)
# assert response.request.path == 'https://www.yahoo.com/'
# class TestClientUtils(TestCase):
# def test_assert_redirects(self):
# response = self.client.get("'/yah44'")
# self.assertRedirects(response, "https://www.yahoo.com/")
if __name__ == '__main__':
unittest.main()
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment