Skip to content

Commit

Permalink
Features/admin sort (#304)
Browse files Browse the repository at this point in the history
  • Loading branch information
rsolovyeaws authored May 25, 2024
2 parents 7bb4250 + 8ac786f commit 3c6f639
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 32 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,6 @@ dmypy.json

#my
db-data/

.idea/
.vscode/
12 changes: 12 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[flake8]
max-line-length = 1000
exclude =
.github
compose
env
venv
logs
static_content
migrations
template
static
6 changes: 3 additions & 3 deletions src/grabber/nsreg/base_site_spider.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
}


# Функция поиска цен в тексте, используя регулярное выражение
def find_price(re_pattern, price):
"""Функция поиска цен в тексте, используя регулярное выражение"""
price = str(price).strip()
if "бесплатн" in price.casefold():
price = 0
Expand All @@ -30,8 +30,8 @@ def find_price(re_pattern, price):
return price


# Класс, реализующий основные компоненты паука для веб-скрапинга
class BaseSpiderComponent:
"""Класс, реализующий основные компоненты паука для веб-скрапинга"""

def __init__(self, start_urls=None, allowed_domains=None, site_names=None, regex=None, path=None):
# Разделение строк по запятым и преобразуем их в списки
Expand All @@ -50,8 +50,8 @@ def __init__(self, start_urls=None, allowed_domains=None, site_names=None, regex
self.regex = regex
self.path = path

# Функция для обработки полученных данных
def parse(self, response):
"""Функция для обработки полученных данных"""
# Поиск цены на регистрацию домена на веб-странице
price_reg = response.xpath(self.path['price_reg']).get()
price_reg = find_price(self.regex['price_reg'], price_reg)
Expand Down
2 changes: 1 addition & 1 deletion src/grabber/nsreg/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# УДАЛИТЬ ПОСЛЕ РЕФАКТОРА СПАЙДЕРОВ
# TODO: УДАЛИТЬ ПОСЛЕ РЕФАКТОРА СПАЙДЕРОВ
import logging
import re

Expand Down
5 changes: 2 additions & 3 deletions src/grabber/nsreg/utils_spider.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# УДАЛИТЬ ПОСЛЕ РЕФАКТОРА СПАЙДЕРОВ
# TODO: УДАЛИТЬ ПОСЛЕ РЕФАКТОРА СПАЙДЕРОВ
from nsreg.items import NsregItem

from nsreg.utils import find_price
Expand Down Expand Up @@ -63,10 +63,9 @@ def moscow_price(self, response, re_pattern, name):

return item


# Exaple site
# https://clustered.ru/#price


def moscow_rich_price(self, response, re_pattern, name):
price_reg = response.xpath(
'/html/body/div[1]/div[4]/div/div[2]/div[3]/div/div/div/div[2]/div/div/\
Expand Down
6 changes: 2 additions & 4 deletions src/telegram_bot/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@


def cut_log(log: str) -> str:
"""
Make log shorter if len > 4000
"""
"""Make log shorter if len > 4000"""
log = log.split("---SPLIT---")[-1]
if len(log) > 4000:
log = f"{log[:4001]}..."
Expand All @@ -37,7 +35,6 @@ def cut_log(log: str) -> str:

@router.message(Command(commands=["start"]))
async def command_start_handler(*args, **kwargs) -> None:

await bot.send_message(
chat_id=CHAT_ID,
message_thread_id=TOPIC_SUPPORT_ID,
Expand Down Expand Up @@ -77,6 +74,7 @@ async def main() -> None:
if not bot_is_done:
await dp.start_polling(bot)


if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
asyncio.run(main())
105 changes: 88 additions & 17 deletions src/website/catalog/admin.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,116 @@
from django.contrib import admin
from django.contrib.auth.models import User, Group
from django.db.models import Subquery, OuterRef, Q
from django.utils.html import format_html

from .models import Registrator, Price, TeamMember

DATE_TIME_FORMAT = "%m/%d/%Y, %H:%M:%S"

admin.site.empty_value_display = "Нет данных"


@admin.register(TeamMember)
class TeamMemberAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'title', 'contact', 'photo', 'sex')
search_fields = ('name', 'title', 'contact', 'sex')


admin.site.register(TeamMember, TeamMemberAdmin)
list_display = ("id", "name", "title", "contact", "photo", "sex")
search_fields = ("name", "title", "contact", "sex")


class PriceRegistratorInline(admin.TabularInline):
model = Price
readonly_fields = ("last_change_at", "parse_at",)
fields = ("last_change_at", "parse_at", "domain",
"price_reg", "reg_status", "price_prolong", "prolong_status", "price_change", "change_status")
readonly_fields = ("last_change_at", "parse_at")
fields = (
"last_change_at",
"parse_at",
"domain",
"price_reg",
"reg_status",
"price_prolong",
"prolong_status",
"price_change",
"change_status",
)
extra = 1
min_num = 1
ordering = ("-parse__date",)

@admin.display(description="Дата парсинга")
def parse_at(self, obj):
return obj.parse.date.strftime("%m/%d/%Y, %H:%M:%S")
return obj.parse.date.strftime(DATE_TIME_FORMAT)

@admin.display(description="Последнее изменение")
def last_change_at(self, obj):
return obj.updated_at.strftime("%m/%d/%Y, %H:%M:%S")
return obj.updated_at.strftime(DATE_TIME_FORMAT)


class EmptyPriceFilter(admin.SimpleListFilter):
title = "Стоимость"
parameter_name = "empty_price"

def lookups(self, request, model_admin):
return (
("yes", "Нет данных"),
("no", "Стоимость указана"),
)

def queryset(self, request, queryset):
is_empty = (
Q(last_reg__isnull=True)
| Q(last_prolong__isnull=True)
| Q(last_change__isnull=True)
)
if self.value() == "yes":
return queryset.filter(is_empty)
elif self.value() == "no":
return queryset.exclude(is_empty)


@admin.register(Registrator)
class RegistratorAdmin(admin.ModelAdmin):
inlines = (PriceRegistratorInline, )
list_display = ("name", "website", "city", "price")
list_filter = ("city",)

@admin.display(description="Цены регистрации")
def price(self, obj):
return list(obj.price_set.values_list("price_reg", flat=True))[-3:]
inlines = (PriceRegistratorInline,)
list_display = (
"name",
"website_link",
"city",
"price_reg",
"price_prolong",
"price_change",
)
list_filter = ("city", EmptyPriceFilter)

@admin.display(description="Сайт")
def website_link(self, obj):
return format_html(
"<a href='{0}' target='_blank'>{0}</a>", obj.website
)

def get_latest_price_subquery(self, field_name):
return Subquery(
Price.objects.filter(registrator=OuterRef("pk"))
.order_by("-id")
.values(field_name)[:1]
)

def get_queryset(self, request):
queryset = super().get_queryset(request)
latest_price_fields = {
"last_reg": self.get_latest_price_subquery("price_reg"),
"last_prolong": self.get_latest_price_subquery("price_prolong"),
"last_change": self.get_latest_price_subquery("price_change"),
}
return queryset.annotate(**latest_price_fields)

@admin.display(description="Регистрация", ordering="last_reg")
def price_reg(self, obj):
return obj.last_reg

@admin.display(description="Продление", ordering="last_prolong")
def price_prolong(self, obj):
return obj.last_prolong

@admin.display(description="Перенос", ordering="last_change")
def price_change(self, obj):
return obj.last_change


admin.site.site_title = "Администрирование Ecodomen"
Expand Down
3 changes: 2 additions & 1 deletion src/website/catalog/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ class Meta:
class Parser(models.Model):
id = models.BigAutoField(primary_key=True)
registrator = models.ForeignKey(Registrator, on_delete=models.CASCADE)
user = models.ForeignKey('auth.User', on_delete=models.CASCADE) # Assuming the use of Django's default user model
# Assuming the use of Django's default user model
user = models.ForeignKey('auth.User', on_delete=models.CASCADE)
contributor_name = models.CharField(max_length=255)
email = models.CharField(max_length=255)
comment = models.TextField()
Expand Down
3 changes: 0 additions & 3 deletions src/website/catalog/tests.py

This file was deleted.

0 comments on commit 3c6f639

Please sign in to comment.