Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 0 additions & 47 deletions .gitignore

This file was deleted.

15 changes: 15 additions & 0 deletions News_Assistant/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Email Configuration
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_USE_SSL = False

# Thay thế bằng Gmail của bạn
EMAIL_HOST_USER = 'phantonbasang@gmail.com'

# Thay thế bằng mật khẩu ứng dụng từ Google Account
EMAIL_HOST_PASSWORD = 'swer dhtw xpbi bjjs'

# Tên hiển thị trong email
DEFAULT_FROM_EMAIL = 'RAUCON_VN News <phantonbasang@gmail.com>'
35 changes: 1 addition & 34 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
# Hướng dẫn cài đặt và chạy dự án web

## Yêu cầu hệ thống
- Python 3.8 trở lên
- pip (Python package installer)
- Git (tùy chọn, để clone repository)

## Các bước cài đặt

### 1. Clone repository (nếu sử dụng Git)
### 1. Clone repository
```bash
git clone <repository-url>
cd <project-directory>
Expand Down Expand Up @@ -51,30 +45,3 @@ python manage.py runserver

### 8. Truy cập website
- Mở trình duyệt web và truy cập: `http://127.0.0.1:8000`
- Đăng nhập với tài khoản superuser đã tạo ở bước 6

## Cấu trúc thư mục
```
project/
├── config/ # Cấu hình chính của dự án
├── home/ # Ứng dụng chính
├── static/ # File tĩnh (CSS, JS, images)
├── templates/ # Templates HTML
├── manage.py # Script quản lý Django
└── requirements.txt # Danh sách các thư viện cần thiết
```

## Xử lý lỗi thường gặp
1. Nếu gặp lỗi "ModuleNotFoundError":
- Kiểm tra môi trường ảo đã được kích hoạt chưa
- Chạy lại lệnh `pip install -r requirements.txt`

2. Nếu gặp lỗi database:
- Kiểm tra cấu hình database trong `settings.py`
- Chạy lại các lệnh migration

3. Nếu gặp lỗi static files:
- Chạy lệnh `python manage.py collectstatic`

## Liên hệ
Nếu bạn gặp bất kỳ vấn đề nào trong quá trình cài đặt, vui lòng liên hệ với quản trị viên hệ thống.
Binary file added db.sqlite3
Binary file not shown.
Binary file added home/__pycache__/__init__.cpython-310.pyc
Binary file not shown.
Binary file added home/__pycache__/admin.cpython-310.pyc
Binary file not shown.
Binary file added home/__pycache__/apps.cpython-310.pyc
Binary file not shown.
Binary file added home/__pycache__/custom_field.cpython-310.pyc
Binary file not shown.
Binary file added home/__pycache__/define.cpython-310.pyc
Binary file not shown.
Binary file added home/__pycache__/helpers.cpython-310.pyc
Binary file not shown.
Binary file added home/__pycache__/models.cpython-310.pyc
Binary file not shown.
Binary file added home/__pycache__/rss_classifier.cpython-310.pyc
Binary file not shown.
Binary file added home/__pycache__/signals.cpython-310.pyc
Binary file not shown.
Binary file added home/__pycache__/urls.cpython-310.pyc
Binary file not shown.
Binary file added home/__pycache__/views.cpython-310.pyc
Binary file not shown.
116 changes: 116 additions & 0 deletions home/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
from django.contrib import admin
from django.core.mail import send_mail
from django.utils import timezone
from django.conf import settings
from .define import *

from .models import Category, Article, Feed, Author, CategoryFeed, Contact

class CategoryAdmin (admin.ModelAdmin):
list_display = ('name', 'status', 'is_homepage', 'layout', 'ordering')
#prepopulated_fields = {'slug':('name',)} #tạo ra mối quan hệ giữa 2 cái này để nó chạy tự động
list_filter = ["is_homepage","status","layout"]
search_fields = ['name']

class Media:
js = ADMIN_SRC_JS
css = ADMIN_SRC_CSS

class ArticleAdmin (admin.ModelAdmin):
list_display = ('name', 'author', 'category', 'status', 'ordering')
#prepopulated_fields = {'slug':('name',)} #tạo ra mối quan hệ giữa 2 cái này để nó chạy tự động
list_filter = ["status", "special", "category", "author"]
search_fields = ['name', 'content']

class Media:
js = ADMIN_SRC_JS
css = ADMIN_SRC_CSS

class FeedAdmin (admin.ModelAdmin):
list_display = ('name', 'status', 'ordering')
#prepopulated_fields = {'slug':('name',)} #tạo ra mối quan hệ giữa 2 cái này để nó chạy tự động
list_filter = ["status"]
search_fields = ['name']

class Media:
js = ADMIN_SRC_JS

# Thêm AuthorAdmin mới
class AuthorAdmin(admin.ModelAdmin):
list_display = ('name', 'email', 'status')
list_filter = ["status"]
search_fields = ['name', 'email']

class Media:
js = ADMIN_SRC_JS
css = ADMIN_SRC_CSS
# thêm CategoryFeed cho RSS_Danh_Muc
class CategoryFeedAdmin(admin.ModelAdmin):
list_display = ('name', 'category', 'status', 'ordering')
list_filter = ('status', 'category')
search_fields = ('name',)

@admin.register(Contact)
class ContactAdmin(admin.ModelAdmin):
list_display = ('name', 'email', 'subject', 'created_at', 'status')
list_filter = ('status', 'created_at')
search_fields = ('name', 'email', 'subject', 'message')
readonly_fields = ('created_at',)
fieldsets = (
('Thông tin người gửi', {
'fields': ('name', 'email', 'created_at')
}),
('Nội dung tin nhắn', {
'fields': ('subject', 'message')
}),
('Phản hồi', {
'fields': ('status', 'reply', 'replied_at')
}),
)

def save_model(self, request, obj, form, change):
if change and 'reply' in form.changed_data: # Nếu phản hồi được thay đổi
obj.status = 'replied'
obj.replied_at = timezone.now()

# Gửi email phản hồi
email_subject = f'[RAUCON_VN] Phản hồi cho tin nhắn: {obj.subject}'
email_message = f"""
Xin chào {obj.name},

Cảm ơn bạn đã liên hệ với RAUCON_VN. Dưới đây là phản hồi cho tin nhắn của bạn:

Tin nhắn của bạn:
{obj.message}

Phản hồi của chúng tôi:
{obj.reply}

Trân trọng,
Đội ngũ RAUCON_VN
"""

try:
send_mail(
subject=email_subject,
message=email_message,
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=[obj.email],
fail_silently=False,
)
except Exception as e:
self.message_user(request, f"Không thể gửi email: {str(e)}", level='ERROR')
else:
self.message_user(request, "Phản hồi đã được gửi qua email thành công!", level='SUCCESS')

super().save_model(request, obj, form, change)

admin.site.register(Category, CategoryAdmin)
admin.site.register(Article, ArticleAdmin)
admin.site.register(Feed, FeedAdmin)
admin.site.register(Author, AuthorAdmin)
admin.site.register(CategoryFeed, CategoryFeedAdmin)
# chỉnh tên trang admin thay vì để mặt định là Django
admin.site.site_header = ADMIN_HEADER_NAME
admin.site.site_title = ADMIN_HEADER_NAME
admin.site.index_title = 'Dashboard'
9 changes: 9 additions & 0 deletions home/custom_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.forms.widgets import RadioSelect
from django.db import models

class CustomBooleanField (models.BooleanField):
def formfield(self, **kwargs):
kwargs['widget'] = RadioSelect (choices= ((True,'Có'), (False, 'Không')))
kwargs['initial'] = False

return super().formfield(**kwargs)
22 changes: 22 additions & 0 deletions home/define.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# models.py
APP_VALUE_LAYOUT_DEFAULT = "list"
APP_VALUE_STATUS_DEFAULT = 'draft'

APP_VALUE_STATUS_CHOICES = (
('draft', 'Draft'),
('published', 'Published')
)

APP_VALUE_LAYOUT_CHOICES = (
('list', 'List'),
('grid', 'Grid')
)

TABLE_CATEGORY_SHOW = "Category"
TABLE_ARTICLE_SHOW = "Article"
TABLE_FEED_SHOW = "Feed"

# admin.py
ADMIN_SRC_JS = ('my_admin/js/jquery-3.6.0.min.js','my_admin/js/slugify.min.js','my_admin/js/general.js')
ADMIN_SRC_CSS = {'all' : ('my_admin/css/custom.css',)}
ADMIN_HEADER_NAME = "news administration "
7 changes: 7 additions & 0 deletions home/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import os
import uuid

def get_file_path (instance, filename):
ext = filename.split ('.')[-1]
filename = '%s.%s' % (uuid.uuid4(),ext)
return os.path.join ('news_app/image/article/',filename)
64 changes: 64 additions & 0 deletions home/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Generated by Django 5.0.2 on 2025-03-30 00:38

import django.db.models.deletion
import home.custom_field
import tinymce.models
from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='Category',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, unique=True)),
('slug', models.CharField(max_length=100, unique=True)),
('is_homepage', home.custom_field.CustomBooleanField()),
('layout', models.CharField(choices=[('list', 'List'), ('grid', 'Grid')], default='list', max_length=10)),
('status', models.CharField(choices=[('draft', 'Draft'), ('published', 'Published')], default='draft', max_length=10)),
('ordering', models.IntegerField(default=0)),
],
options={
'verbose_name_plural': 'Category',
},
),
migrations.CreateModel(
name='Feed',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, unique=True)),
('slug', models.CharField(max_length=100, unique=True)),
('status', models.CharField(choices=[('draft', 'Draft'), ('published', 'Published')], default='draft', max_length=10)),
('ordering', models.IntegerField(default=0)),
('link', models.CharField(max_length=250)),
],
options={
'verbose_name_plural': 'Feed',
},
),
migrations.CreateModel(
name='Article',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, unique=True)),
('slug', models.CharField(max_length=100, unique=True)),
('status', models.CharField(choices=[('draft', 'Draft'), ('published', 'Published')], default='draft', max_length=10)),
('ordering', models.IntegerField(default=0)),
('special', home.custom_field.CustomBooleanField()),
('publish_date', models.DateTimeField()),
('content', tinymce.models.HTMLField()),
('image', models.ImageField(upload_to='news_app/image/article/')),
('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='home.category')),
],
options={
'verbose_name_plural': 'Article',
},
),
]
Loading