mirror of
https://github.com/Sosokker/ku-polls.git
synced 2025-12-18 13:04:05 +01:00
Merge pull request #52 from Sosokker/iteration4 | Format Code, Add Search + Tag + Poll Creation
Iteration4 Format Code, Add Search + Tag + Poll Creation
This commit is contained in:
commit
c9466b686d
12
.flake8
Normal file
12
.flake8
Normal file
@ -0,0 +1,12 @@
|
||||
[flake8]
|
||||
max-line-length = 120
|
||||
exclude=
|
||||
migrations
|
||||
mysite
|
||||
logs
|
||||
.venv
|
||||
apps.py
|
||||
setup.py
|
||||
per-file-ignores =
|
||||
.\polls\tests\test_sentiment_model.py: E501
|
||||
.\polls\models.py: F811
|
||||
97
Installation.md
Normal file
97
Installation.md
Normal file
@ -0,0 +1,97 @@
|
||||
## Installation
|
||||
|
||||
There are two ways to install and run this project.
|
||||
|
||||
1. Use `setup.py` or `setup.ps1`(for windows) to install the project.
|
||||
2. Manually install the project with the Instruction in section 2.
|
||||
|
||||
### 1. Use `setup.py` or `setup.ps1`(for windows) to install the project.
|
||||
1. Install [Python 3.11 or later](https://www.python.org/downloads/)
|
||||
2. Clone this repository and Run `setup.py` to install and run the project
|
||||
|
||||
**Don't forget to answer the question from `setup.py` to setup the project**
|
||||
|
||||
This method will autogenerate Environment Variable for you.
|
||||
```bash
|
||||
git clone https://github.com/Sosokker/ku-polls
|
||||
cd ku-polls
|
||||
python setup.py
|
||||
```
|
||||
If you want to customize the environment variables, name of environment folder then run this command
|
||||
```bash
|
||||
python setup.py -custom
|
||||
```
|
||||
or run `setup.ps1` (For Windows User)
|
||||
|
||||
----
|
||||
|
||||
### 2. Manually install the project with this instruction.
|
||||
|
||||
1. Install [Python 3.11 or later](https://www.python.org/downloads/)
|
||||
2. Run these commands to clone this repository and enter the directory.
|
||||
```bash
|
||||
git clone https://github.com/Sosokker/ku-polls
|
||||
cd ku
|
||||
```
|
||||
|
||||
3. (Optional: You can use venv instead)Install virtualenv via pip
|
||||
|
||||
```bash
|
||||
python -m pip install --user virtualenv
|
||||
```
|
||||
4. Create virtual environment with `venv` or `virtualenv`.
|
||||
```bash
|
||||
python -m virtualenv .venv
|
||||
or
|
||||
python -m venv .venv
|
||||
```
|
||||
5. Use `virtual environment`
|
||||
|
||||
- Windows
|
||||
```bash
|
||||
.venv\Scripts\activate
|
||||
```
|
||||
|
||||
- Linux or MacOS
|
||||
```bash
|
||||
source .venv/bin/activate
|
||||
```
|
||||
6. Install require module.
|
||||
```
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
7. Create file call `.env` in `ku-polls` directory and add this line
|
||||
**You can look at `sample.env` for more information and others environment variables to set.**
|
||||
```bash
|
||||
SECRET_KEY=your_secret_key
|
||||
```
|
||||
|
||||
You can generate your own `your_secret_key` by this command
|
||||
```bash
|
||||
python manage.py shell -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"
|
||||
```
|
||||
or
|
||||
- [Django Secret Key Generator #1](https://djecrety.ir/)
|
||||
- [Django Secret Key Generator #2](https://miniwebtool.com/django-secret-key-generator/)
|
||||
|
||||
**Don't forget to change `your_secret_key` to your secret key (without quote)**
|
||||
|
||||
8. Migrate database and load data into it then runserver.
|
||||
```bash
|
||||
python manage.py migrate
|
||||
python manage.py loaddata data/users.json
|
||||
python manage.py loaddata data/polls.json
|
||||
python manage.py runserver
|
||||
```
|
||||
|
||||
***NOTE***
|
||||
|
||||
By Default `DEBUG=False` and Django will not load Static files for you so if you want to apply CSS run this.
|
||||
```bash
|
||||
python manage.py runserver --insecure
|
||||
```
|
||||
or set `DEBUG=True`
|
||||
or do the [collectstatic](https://docs.djangoproject.com/en/4.2/ref/contrib/staticfiles/)
|
||||
|
||||
Then connect to [http://127.0.0.1:8000/](http://127.0.0.1:8000/) or [localhost:8000/](localhost:8000/)
|
||||
1265
data/alldata.json
Normal file
1265
data/alldata.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,180 +0,0 @@
|
||||
[
|
||||
{
|
||||
"model": "polls.question",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"question_text": "Python vs C++, which one is better in your opinion?",
|
||||
"short_description": "Cool kids have polls",
|
||||
"long_description": "No description provide for this poll.",
|
||||
"pub_date": "2023-08-28T13:38:42Z",
|
||||
"end_date": "2025-09-24T22:56:52Z",
|
||||
"up_vote_count": 0,
|
||||
"down_vote_count": 0,
|
||||
"participant_count": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.question",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"question_text": "The chicken and the egg, which came first?",
|
||||
"short_description": "Cool kids have polls",
|
||||
"long_description": "No description provide for this poll.",
|
||||
"pub_date": "2023-08-28T13:39:16Z",
|
||||
"end_date": "2023-11-05T06:30:16Z",
|
||||
"up_vote_count": 12,
|
||||
"down_vote_count": 4,
|
||||
"participant_count": 11
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.question",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"question_text": "Do you love Django?",
|
||||
"short_description": "Cool kids have polls",
|
||||
"long_description": "No description provide for this poll.",
|
||||
"pub_date": "2023-11-07T15:40:31Z",
|
||||
"end_date": "2023-09-20T13:41:33Z",
|
||||
"up_vote_count": 3000,
|
||||
"down_vote_count": 50,
|
||||
"participant_count": 555
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.question",
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"question_text": "So far so good?",
|
||||
"short_description": "Cool kids have polls",
|
||||
"long_description": "No description provide for this poll.",
|
||||
"pub_date": "2023-09-09T06:30:50.690Z",
|
||||
"end_date": "2023-09-30T12:54:30Z",
|
||||
"up_vote_count": 0,
|
||||
"down_vote_count": 0,
|
||||
"participant_count": 234
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.question",
|
||||
"pk": 5,
|
||||
"fields": {
|
||||
"question_text": "Do you love Django?",
|
||||
"short_description": "Cool kids have polls",
|
||||
"long_description": "No description provide for this poll.",
|
||||
"pub_date": "2023-09-09T13:42:27.501Z",
|
||||
"end_date": "2023-09-21T13:42:17Z",
|
||||
"up_vote_count": 1,
|
||||
"down_vote_count": 1,
|
||||
"participant_count": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"question": 2,
|
||||
"choice_text": "Chicken",
|
||||
"votes": 55
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 5,
|
||||
"fields": {
|
||||
"question": 2,
|
||||
"choice_text": "Egg",
|
||||
"votes": 56
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 6,
|
||||
"fields": {
|
||||
"question": 1,
|
||||
"choice_text": "Python!",
|
||||
"votes": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 7,
|
||||
"fields": {
|
||||
"question": 1,
|
||||
"choice_text": "C++",
|
||||
"votes": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 8,
|
||||
"fields": {
|
||||
"question": 3,
|
||||
"choice_text": "Yeah for sure!",
|
||||
"votes": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 9,
|
||||
"fields": {
|
||||
"question": 3,
|
||||
"choice_text": "nah",
|
||||
"votes": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 13,
|
||||
"fields": {
|
||||
"question": 4,
|
||||
"choice_text": "Yes!",
|
||||
"votes": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 14,
|
||||
"fields": {
|
||||
"question": 4,
|
||||
"choice_text": "No!",
|
||||
"votes": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 15,
|
||||
"fields": {
|
||||
"question": 4,
|
||||
"choice_text": "Okay!",
|
||||
"votes": 5
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 16,
|
||||
"fields": {
|
||||
"question": 4,
|
||||
"choice_text": "Thank you!",
|
||||
"votes": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 17,
|
||||
"fields": {
|
||||
"question": 5,
|
||||
"choice_text": "Yeah for sure!",
|
||||
"votes": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 18,
|
||||
"fields": {
|
||||
"question": 5,
|
||||
"choice_text": "nah",
|
||||
"votes": 2
|
||||
}
|
||||
}
|
||||
]
|
||||
649
data/polls.json
649
data/polls.json
@ -9,7 +9,9 @@
|
||||
"short_description": "🐍 vs 🇨 ++",
|
||||
"long_description": "Let them fight.",
|
||||
"trend_score": 60.0,
|
||||
"tags": []
|
||||
"tags": [
|
||||
7
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -22,7 +24,9 @@
|
||||
"short_description": "Classic question.",
|
||||
"long_description": "Most biologists state unequivocally that the egg came first. At their most basic level, eggs are just female sex cells. Hard external eggs that can be laid on land (also known as amniotic eggs) were a game changer for vertebrates. Anyway, let select you choice.",
|
||||
"trend_score": 140.0,
|
||||
"tags": []
|
||||
"tags": [
|
||||
4
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -35,7 +39,9 @@
|
||||
"short_description": "If you know, you know.",
|
||||
"long_description": "The sentence where you will hear once every week.",
|
||||
"trend_score": 120.0,
|
||||
"tags": []
|
||||
"tags": [
|
||||
6
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -48,7 +54,9 @@
|
||||
"short_description": "Djago, I love it!",
|
||||
"long_description": "Django is a high-level Python web framework that encourages rapid development and clean, pragmatic design. Built by experienced developers, it takes care of much of the hassle of web development, so you can focus on writing your app without needing to reinvent the wheel. It’s free and open source.",
|
||||
"trend_score": 120.0,
|
||||
"tags": []
|
||||
"tags": [
|
||||
7
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -61,7 +69,9 @@
|
||||
"short_description": "Swifty vs Ye Club",
|
||||
"long_description": "It's no secret that Taylor Swift and Kanye West's relationship can be described as volatile at best. Let choose!",
|
||||
"trend_score": 100.0,
|
||||
"tags": []
|
||||
"tags": [
|
||||
8
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -74,7 +84,9 @@
|
||||
"short_description": "Please do my poll🥺",
|
||||
"long_description": "My favorite code editor is VScode. What about you?",
|
||||
"trend_score": 100.0,
|
||||
"tags": []
|
||||
"tags": [
|
||||
7
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -82,12 +94,15 @@
|
||||
"pk": 8,
|
||||
"fields": {
|
||||
"question_text": "Which one is the best RIZZ line?",
|
||||
"pub_date": "2023-09-14T15:52:03Z",
|
||||
"end_date": "2023-11-14T17:00:00Z",
|
||||
"pub_date": "2023-09-17T16:39:52Z",
|
||||
"end_date": "2023-12-12T17:00:00Z",
|
||||
"short_description": "RIZZ (noun) How good you are with flirting",
|
||||
"long_description": "Rizz actually comes from the word charisma. It's a NYC Slang created by a twitch streamer named Kai cenat. This means that you have game, a good flirt line.",
|
||||
"trend_score": 100.0,
|
||||
"tags": []
|
||||
"tags": [
|
||||
5,
|
||||
6
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -100,7 +115,24 @@
|
||||
"short_description": "Pizza without topping is not pizza!",
|
||||
"long_description": "I mean, Yeah here are the choices ;)",
|
||||
"trend_score": 100.0,
|
||||
"tags": []
|
||||
"tags": [
|
||||
4
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.question",
|
||||
"pk": 14,
|
||||
"fields": {
|
||||
"question_text": "What is your favorite subjects?",
|
||||
"pub_date": "2023-09-17T16:37:49Z",
|
||||
"end_date": "2023-11-29T16:37:53Z",
|
||||
"short_description": "Which subjects do you love?",
|
||||
"long_description": "Here are the list of subjects, which one is the best for you!",
|
||||
"trend_score": 100.0,
|
||||
"tags": [
|
||||
9
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -196,7 +228,7 @@
|
||||
"pk": 12,
|
||||
"fields": {
|
||||
"question": 6,
|
||||
"choice_text": "Neither"
|
||||
"choice_text": "Nobody is better than others."
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -228,7 +260,7 @@
|
||||
"pk": 16,
|
||||
"fields": {
|
||||
"question": 7,
|
||||
"choice_text": "IntelliJ IDEA"
|
||||
"choice_text": "JetBrains IDE"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -368,462 +400,245 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 1,
|
||||
"model": "polls.choice",
|
||||
"pk": 38,
|
||||
"fields": {
|
||||
"choice": 1,
|
||||
"user": 1,
|
||||
"question": 1
|
||||
"question": 9,
|
||||
"choice_text": "Pepperoni"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 2,
|
||||
"model": "polls.choice",
|
||||
"pk": 39,
|
||||
"fields": {
|
||||
"choice": 1,
|
||||
"user": 3,
|
||||
"question": 1
|
||||
"question": 9,
|
||||
"choice_text": "Chicken breast"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 3,
|
||||
"model": "polls.choice",
|
||||
"pk": 40,
|
||||
"fields": {
|
||||
"choice": 2,
|
||||
"user": 2,
|
||||
"question": 1
|
||||
"question": 9,
|
||||
"choice_text": "Mushrooms"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 4,
|
||||
"model": "polls.choice",
|
||||
"pk": 41,
|
||||
"fields": {
|
||||
"choice": 7,
|
||||
"user": 2,
|
||||
"question": 4
|
||||
"question": 9,
|
||||
"choice_text": "Tomato"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 5,
|
||||
"model": "polls.choice",
|
||||
"pk": 42,
|
||||
"fields": {
|
||||
"choice": 6,
|
||||
"user": 2,
|
||||
"question": 3
|
||||
"question": 9,
|
||||
"choice_text": "Onions"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 6,
|
||||
"model": "polls.choice",
|
||||
"pk": 43,
|
||||
"fields": {
|
||||
"choice": 3,
|
||||
"user": 2,
|
||||
"question": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 7,
|
||||
"fields": {
|
||||
"choice": 8,
|
||||
"user": 4,
|
||||
"question": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 8,
|
||||
"fields": {
|
||||
"choice": 6,
|
||||
"user": 4,
|
||||
"question": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 9,
|
||||
"fields": {
|
||||
"choice": 3,
|
||||
"user": 4,
|
||||
"question": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 10,
|
||||
"fields": {
|
||||
"choice": 2,
|
||||
"user": 4,
|
||||
"question": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 11,
|
||||
"fields": {
|
||||
"choice": 6,
|
||||
"user": 3,
|
||||
"question": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 12,
|
||||
"fields": {
|
||||
"choice": 3,
|
||||
"user": 3,
|
||||
"question": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 13,
|
||||
"fields": {
|
||||
"choice": 6,
|
||||
"user": 6,
|
||||
"question": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 14,
|
||||
"fields": {
|
||||
"choice": 7,
|
||||
"user": 3,
|
||||
"question": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 15,
|
||||
"fields": {
|
||||
"choice": 2,
|
||||
"user": 6,
|
||||
"question": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 16,
|
||||
"fields": {
|
||||
"choice": 9,
|
||||
"user": 1,
|
||||
"question": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 17,
|
||||
"fields": {
|
||||
"choice": 6,
|
||||
"user": 1,
|
||||
"question": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 18,
|
||||
"fields": {
|
||||
"choice": 25,
|
||||
"user": 2,
|
||||
"question": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 19,
|
||||
"fields": {
|
||||
"choice": 15,
|
||||
"user": 2,
|
||||
"question": 7
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 20,
|
||||
"fields": {
|
||||
"choice": 24,
|
||||
"user": 4,
|
||||
"question": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 21,
|
||||
"fields": {
|
||||
"choice": 25,
|
||||
"user": 3,
|
||||
"question": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 22,
|
||||
"fields": {
|
||||
"choice": 14,
|
||||
"user": 3,
|
||||
"question": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 23,
|
||||
"fields": {
|
||||
"choice": 28,
|
||||
"user": 3,
|
||||
"question": 9
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 24,
|
||||
"fields": {
|
||||
"choice": 27,
|
||||
"user": 6,
|
||||
"question": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 25,
|
||||
"fields": {
|
||||
"choice": 15,
|
||||
"user": 6,
|
||||
"question": 7
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 26,
|
||||
"fields": {
|
||||
"choice": 29,
|
||||
"user": 6,
|
||||
"question": 9
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 27,
|
||||
"fields": {
|
||||
"choice": 26,
|
||||
"user": 1,
|
||||
"question": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"user": 1,
|
||||
"question": 1,
|
||||
"vote_types": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"user": 2,
|
||||
"question": 2,
|
||||
"vote_types": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"user": 2,
|
||||
"question": 4,
|
||||
"vote_types": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"user": 2,
|
||||
"question": 3,
|
||||
"vote_types": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 5,
|
||||
"fields": {
|
||||
"user": 2,
|
||||
"question": 1,
|
||||
"vote_types": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 6,
|
||||
"fields": {
|
||||
"user": 6,
|
||||
"question": 1,
|
||||
"vote_types": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 7,
|
||||
"fields": {
|
||||
"user": 6,
|
||||
"question": 3,
|
||||
"vote_types": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 8,
|
||||
"fields": {
|
||||
"user": 6,
|
||||
"question": 4,
|
||||
"vote_types": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 9,
|
||||
"fields": {
|
||||
"user": 3,
|
||||
"question": 1,
|
||||
"vote_types": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 10,
|
||||
"fields": {
|
||||
"user": 3,
|
||||
"question": 3,
|
||||
"vote_types": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 11,
|
||||
"fields": {
|
||||
"user": 3,
|
||||
"question": 4,
|
||||
"vote_types": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 12,
|
||||
"fields": {
|
||||
"user": 3,
|
||||
"question": 2,
|
||||
"vote_types": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 14,
|
||||
"fields": {
|
||||
"user": 1,
|
||||
"question": 4,
|
||||
"vote_types": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 15,
|
||||
"fields": {
|
||||
"user": 1,
|
||||
"question": 3,
|
||||
"vote_types": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 16,
|
||||
"fields": {
|
||||
"user": 1,
|
||||
"question": 2,
|
||||
"vote_types": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 17,
|
||||
"fields": {
|
||||
"user": 2,
|
||||
"question": 8,
|
||||
"vote_types": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 18,
|
||||
"fields": {
|
||||
"user": 2,
|
||||
"question": 7,
|
||||
"vote_types": true
|
||||
"choice_text": "Sublime Text"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 19,
|
||||
"model": "polls.choice",
|
||||
"pk": 44,
|
||||
"fields": {
|
||||
"user": 4,
|
||||
"question": 8,
|
||||
"vote_types": true
|
||||
"question": 7,
|
||||
"choice_text": "Notepad++"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 20,
|
||||
"model": "polls.choice",
|
||||
"pk": 45,
|
||||
"fields": {
|
||||
"user": 3,
|
||||
"question": 8,
|
||||
"vote_types": true
|
||||
"question": 7,
|
||||
"choice_text": "Atom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 21,
|
||||
"model": "polls.choice",
|
||||
"pk": 46,
|
||||
"fields": {
|
||||
"user": 3,
|
||||
"question": 6,
|
||||
"vote_types": false
|
||||
"question": 7,
|
||||
"choice_text": "Neovim"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 22,
|
||||
"model": "polls.choice",
|
||||
"pk": 47,
|
||||
"fields": {
|
||||
"question": 7,
|
||||
"choice_text": "Notepad"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 48,
|
||||
"fields": {
|
||||
"question": 7,
|
||||
"choice_text": "Visual Studio"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 49,
|
||||
"fields": {
|
||||
"question": 7,
|
||||
"choice_text": "Powerpoint"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 50,
|
||||
"fields": {
|
||||
"question": 3,
|
||||
"choice_text": "Maybe"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 51,
|
||||
"fields": {
|
||||
"question": 14,
|
||||
"choice_text": "Mathematics"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 52,
|
||||
"fields": {
|
||||
"question": 14,
|
||||
"choice_text": "Geography"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 53,
|
||||
"fields": {
|
||||
"question": 14,
|
||||
"choice_text": "Economics"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 54,
|
||||
"fields": {
|
||||
"question": 14,
|
||||
"choice_text": "Physics"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 55,
|
||||
"fields": {
|
||||
"question": 14,
|
||||
"choice_text": "History"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 56,
|
||||
"fields": {
|
||||
"question": 14,
|
||||
"choice_text": "Biology"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 57,
|
||||
"fields": {
|
||||
"question": 14,
|
||||
"choice_text": "Chemistry"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 58,
|
||||
"fields": {
|
||||
"question": 14,
|
||||
"choice_text": "Psychology"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 59,
|
||||
"fields": {
|
||||
"question": 14,
|
||||
"choice_text": "Philosophy"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 60,
|
||||
"fields": {
|
||||
"question": 14,
|
||||
"choice_text": "Other."
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.choice",
|
||||
"pk": 61,
|
||||
"fields": {
|
||||
"user": 3,
|
||||
"question": 9,
|
||||
"vote_types": false
|
||||
"choice_text": "Other"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 23,
|
||||
"model": "polls.choice",
|
||||
"pk": 62,
|
||||
"fields": {
|
||||
"user": 6,
|
||||
"question": 8,
|
||||
"vote_types": true
|
||||
"question": 3,
|
||||
"choice_text": "No Opinion."
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 24,
|
||||
"model": "polls.tag",
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"user": 6,
|
||||
"question": 9,
|
||||
"vote_types": false
|
||||
"tag_text": "Food"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.sentimentvote",
|
||||
"pk": 25,
|
||||
"model": "polls.tag",
|
||||
"pk": 5,
|
||||
"fields": {
|
||||
"user": 1,
|
||||
"question": 8,
|
||||
"vote_types": true
|
||||
"tag_text": "Flirt"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.tag",
|
||||
"pk": 6,
|
||||
"fields": {
|
||||
"tag_text": "Meme"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.tag",
|
||||
"pk": 7,
|
||||
"fields": {
|
||||
"tag_text": "Programming"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.tag",
|
||||
"pk": 8,
|
||||
"fields": {
|
||||
"tag_text": "Singer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.tag",
|
||||
"pk": 9,
|
||||
"fields": {
|
||||
"tag_text": "Education"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"password": "pbkdf2_sha256$600000$aDh9a1PXxcXAb8z3YIjAPX$NVH24kt/wMad+0fZcCii738dfojI4vL2ffXOwNRuLz4=",
|
||||
"last_login": "2023-09-15T17:09:43.310Z",
|
||||
"last_login": "2023-09-17T16:33:12.136Z",
|
||||
"is_superuser": true,
|
||||
"username": "admin",
|
||||
"first_name": "",
|
||||
@ -22,7 +22,7 @@
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"password": "pbkdf2_sha256$600000$quZKLKT8Ec3TQgpdqlCkpX$o+VOOnRDLGf64qjHb239Yvsre74tPkC8hw1qH1un/hk=",
|
||||
"last_login": "2023-09-15T17:07:42.782Z",
|
||||
"last_login": "2023-09-17T16:07:12.447Z",
|
||||
"is_superuser": false,
|
||||
"username": "tester1",
|
||||
"first_name": "",
|
||||
@ -109,18 +109,18 @@
|
||||
},
|
||||
{
|
||||
"model": "auth.user",
|
||||
"pk": 7,
|
||||
"pk": 8,
|
||||
"fields": {
|
||||
"password": "1234",
|
||||
"last_login": null,
|
||||
"password": "pbkdf2_sha256$600000$QL1ktlWKsyyG2IASDFrJTQ$L7vKabykHi4ir3IcCf3qE9tj/TrAyyfQMekQ4xj+s7I=",
|
||||
"last_login": "2023-09-15T17:23:08.479Z",
|
||||
"is_superuser": false,
|
||||
"username": "test",
|
||||
"username": "tester5",
|
||||
"first_name": "",
|
||||
"last_name": "",
|
||||
"email": "",
|
||||
"is_staff": false,
|
||||
"is_active": true,
|
||||
"date_joined": "2023-09-15T11:53:22.035Z",
|
||||
"date_joined": "2023-09-15T17:23:07.965Z",
|
||||
"groups": [],
|
||||
"user_permissions": []
|
||||
}
|
||||
|
||||
263
data/vote.json
Normal file
263
data/vote.json
Normal file
@ -0,0 +1,263 @@
|
||||
[
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 1,
|
||||
"fields": {
|
||||
"choice": 1,
|
||||
"user": 1,
|
||||
"question": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 2,
|
||||
"fields": {
|
||||
"choice": 1,
|
||||
"user": 3,
|
||||
"question": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"choice": 2,
|
||||
"user": 2,
|
||||
"question": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"choice": 7,
|
||||
"user": 2,
|
||||
"question": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 5,
|
||||
"fields": {
|
||||
"choice": 6,
|
||||
"user": 2,
|
||||
"question": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 6,
|
||||
"fields": {
|
||||
"choice": 3,
|
||||
"user": 2,
|
||||
"question": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 7,
|
||||
"fields": {
|
||||
"choice": 8,
|
||||
"user": 4,
|
||||
"question": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 8,
|
||||
"fields": {
|
||||
"choice": 6,
|
||||
"user": 4,
|
||||
"question": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 9,
|
||||
"fields": {
|
||||
"choice": 3,
|
||||
"user": 4,
|
||||
"question": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 10,
|
||||
"fields": {
|
||||
"choice": 2,
|
||||
"user": 4,
|
||||
"question": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 11,
|
||||
"fields": {
|
||||
"choice": 6,
|
||||
"user": 3,
|
||||
"question": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 12,
|
||||
"fields": {
|
||||
"choice": 3,
|
||||
"user": 3,
|
||||
"question": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 13,
|
||||
"fields": {
|
||||
"choice": 6,
|
||||
"user": 6,
|
||||
"question": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 14,
|
||||
"fields": {
|
||||
"choice": 7,
|
||||
"user": 3,
|
||||
"question": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 15,
|
||||
"fields": {
|
||||
"choice": 2,
|
||||
"user": 6,
|
||||
"question": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 16,
|
||||
"fields": {
|
||||
"choice": 9,
|
||||
"user": 1,
|
||||
"question": 4
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 17,
|
||||
"fields": {
|
||||
"choice": 6,
|
||||
"user": 1,
|
||||
"question": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 18,
|
||||
"fields": {
|
||||
"choice": 25,
|
||||
"user": 2,
|
||||
"question": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 19,
|
||||
"fields": {
|
||||
"choice": 15,
|
||||
"user": 2,
|
||||
"question": 7
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 20,
|
||||
"fields": {
|
||||
"choice": 24,
|
||||
"user": 4,
|
||||
"question": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 21,
|
||||
"fields": {
|
||||
"choice": 25,
|
||||
"user": 3,
|
||||
"question": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 22,
|
||||
"fields": {
|
||||
"choice": 14,
|
||||
"user": 3,
|
||||
"question": 6
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 23,
|
||||
"fields": {
|
||||
"choice": 28,
|
||||
"user": 3,
|
||||
"question": 9
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 24,
|
||||
"fields": {
|
||||
"choice": 27,
|
||||
"user": 6,
|
||||
"question": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 25,
|
||||
"fields": {
|
||||
"choice": 15,
|
||||
"user": 6,
|
||||
"question": 7
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 26,
|
||||
"fields": {
|
||||
"choice": 29,
|
||||
"user": 6,
|
||||
"question": 9
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 27,
|
||||
"fields": {
|
||||
"choice": 26,
|
||||
"user": 1,
|
||||
"question": 8
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 28,
|
||||
"fields": {
|
||||
"choice": 28,
|
||||
"user": 1,
|
||||
"question": 9
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "polls.vote",
|
||||
"pk": 29,
|
||||
"fields": {
|
||||
"choice": 12,
|
||||
"user": 1,
|
||||
"question": 6
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -1,6 +1,6 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import Choice, Question
|
||||
from .models import Choice, Question, Tag
|
||||
|
||||
|
||||
class ChoiceInline(admin.TabularInline):
|
||||
@ -15,12 +15,15 @@ class QuestionAdmin(admin.ModelAdmin):
|
||||
("End date", {"fields": ["end_date"], "classes": ["collapse"]}),
|
||||
("Short Description", {"fields": ["short_description"], "classes": ["collapse"]}),
|
||||
("Long Description", {"fields": ["long_description"], "classes": ["collapse"]}),
|
||||
("Add Tag", {"fields": ["tags"], "classes": ["collapse"]})
|
||||
]
|
||||
list_display = ["question_text", "pub_date", "end_date", "was_published_recently", "can_vote", "trending_score"]
|
||||
list_display = ["question_text", "pub_date", "end_date", "was_published_recently", "can_vote",
|
||||
"trending_score", "get_tags"]
|
||||
inlines = [ChoiceInline]
|
||||
list_filter = ["pub_date", "end_date"]
|
||||
search_fields = ["question_text"]
|
||||
|
||||
|
||||
# https://stackoverflow.com/questions/10904848/adding-inline-many-to-many-objects-in-django-admin
|
||||
admin.site.register(Question, QuestionAdmin)
|
||||
|
||||
admin.site.register(Tag) # Add Field to modify tags objects in Question
|
||||
|
||||
@ -4,6 +4,7 @@ from django.apps import AppConfig
|
||||
class PollsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'polls'
|
||||
|
||||
|
||||
def ready(self) -> None:
|
||||
import polls.signals
|
||||
import polls.signals
|
||||
|
||||
@ -2,22 +2,25 @@ import logging
|
||||
from typing import Any
|
||||
|
||||
from django import forms
|
||||
from django.apps import apps
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from .models import Question
|
||||
|
||||
|
||||
class SignUpForm(UserCreationForm):
|
||||
tailwind_class = "w-full border-2 border-gray-300 bg-gray-100 rounded-lg focus:ring focus:border-blue-300 focus:shadow-none"
|
||||
tailwind_class = """w-full border-2 border-gray-300 bg-gray-100 rounded-lg
|
||||
focus:ring focus:border-blue-300 focus:shadow-none"""
|
||||
logger = logging.getLogger('signup_form')
|
||||
username = forms.CharField(widget=forms.TextInput(attrs={'class': tailwind_class}),
|
||||
error_messages={
|
||||
'unique': 'This username is already in use.',
|
||||
'invalid': 'Invalid username format.',
|
||||
'max_length': 'Username should not exceed 150 characters.',
|
||||
username = forms.CharField(widget=forms.TextInput(attrs={'class': tailwind_class}), error_messages={
|
||||
'unique': 'This username is already in use.',
|
||||
'invalid': 'Invalid username format.',
|
||||
'max_length': 'Username should not exceed 150 characters.',
|
||||
}
|
||||
)
|
||||
password1 = forms.CharField(widget=forms.PasswordInput(attrs={'class': tailwind_class}),
|
||||
error_messages={'min_length': 'Password must contain at least 8 characters.',}
|
||||
error_messages={'min_length': 'Password must contain at least 8 characters.', }
|
||||
)
|
||||
password2 = forms.CharField(widget=forms.PasswordInput(attrs={'class': tailwind_class}),)
|
||||
|
||||
@ -37,4 +40,50 @@ class SignUpForm(UserCreationForm):
|
||||
|
||||
error_messages = {
|
||||
'password_mismatch': "The two password fields didn't match.",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PollSearchForm(forms.Form):
|
||||
q = forms.CharField()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class PollCreateForm(forms.ModelForm):
|
||||
box_style = """w-full py-2 px-2 border-2 border-gray-300 bg-gray-100 rounded-lg
|
||||
focus:ring focus:border-blue-300 focus:shadow-none"""
|
||||
large_box_style = """w-full border-2 border-gray-300 bg-gray-100 rounded-lg
|
||||
focus:ring focus:border-blue-300 focus:shadow-none"""
|
||||
|
||||
question_text = forms.CharField(min_length=10, max_length=100, required=True,
|
||||
widget=forms.TextInput(attrs={'class': box_style,
|
||||
'placeholder': "What is your question?"}))
|
||||
pub_date = forms.DateTimeField(widget=forms.DateInput(attrs={'type': 'date'}))
|
||||
end_date = forms.DateTimeField(widget=forms.DateInput(attrs={'type': 'date'}))
|
||||
short_description = forms.CharField(max_length=200,
|
||||
widget=forms.TextInput(
|
||||
attrs={'class': box_style,
|
||||
'placeholder': "Short description (Maximum 200 characters)"}))
|
||||
long_description = forms.CharField(max_length=2000,
|
||||
widget=forms.Textarea(
|
||||
attrs={'class': large_box_style,
|
||||
'placeholder': "Long description (Maximum 2000 characters)"}))
|
||||
user_choice = forms.CharField(
|
||||
widget=forms.TextInput(attrs={'placeholder': 'Enter a choice'}),
|
||||
required=True
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
Tag = apps.get_model('polls', 'Tag')
|
||||
|
||||
tags = forms.MultipleChoiceField(
|
||||
choices=[(tag.id, tag.tag_text) for tag in Tag.objects.all()],
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Question
|
||||
fields = ['question_text', 'pub_date', 'end_date', 'short_description', 'long_description', 'tags']
|
||||
|
||||
@ -12,7 +12,6 @@ Attributes:
|
||||
from django.db import models, IntegrityError
|
||||
from django.utils import timezone
|
||||
from django.contrib import admin
|
||||
from django.db.models import Sum
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
@ -23,7 +22,7 @@ class Tag(models.Model):
|
||||
tag_text = models.CharField(max_length=50)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
return self.tag_text
|
||||
|
||||
|
||||
class Question(models.Model):
|
||||
@ -128,6 +127,7 @@ class Question(models.Model):
|
||||
|
||||
@property
|
||||
def time_left(self):
|
||||
"""Return time till ending of the poll"""
|
||||
return self.calculate_time_left()
|
||||
|
||||
def calculate_vote_percentage(self):
|
||||
@ -143,10 +143,12 @@ class Question(models.Model):
|
||||
|
||||
@property
|
||||
def up_vote_percentage(self):
|
||||
"""Retrieve up vote percentage from calculate_vote_percentage"""
|
||||
return self.calculate_vote_percentage()[0]
|
||||
|
||||
@property
|
||||
def down_vote_percentage(self):
|
||||
"""Retrieve down vote percentage from calculate_vote_percentage"""
|
||||
return self.calculate_vote_percentage()[1]
|
||||
|
||||
@property
|
||||
@ -166,7 +168,7 @@ class Question(models.Model):
|
||||
self.save()
|
||||
except IntegrityError:
|
||||
vote = self.sentimentvote_set.filter(user=user)
|
||||
if vote[0].vote_types == False:
|
||||
if vote[0].vote_types is False:
|
||||
vote.update(vote_types=True)
|
||||
self.save()
|
||||
else:
|
||||
@ -182,7 +184,7 @@ class Question(models.Model):
|
||||
self.save()
|
||||
except IntegrityError:
|
||||
vote = self.sentimentvote_set.filter(user=user)
|
||||
if vote[0].vote_types == True:
|
||||
if vote[0].vote_types is True:
|
||||
vote.update(vote_types=False)
|
||||
self.save()
|
||||
else:
|
||||
@ -191,10 +193,12 @@ class Question(models.Model):
|
||||
|
||||
@property
|
||||
def up_vote_count(self):
|
||||
"""Count up vote of Question"""
|
||||
return self.sentimentvote_set.filter(question=self, vote_types=True).count()
|
||||
|
||||
@property
|
||||
def down_vote_count(self):
|
||||
"""Count down vote of Question"""
|
||||
return self.sentimentvote_set.filter(question=self, vote_types=False).count()
|
||||
|
||||
def trending_score(self, up=None, down=None):
|
||||
@ -202,8 +206,8 @@ class Question(models.Model):
|
||||
published_date_duration = timezone.now() - self.pub_date
|
||||
score = 0
|
||||
|
||||
if (published_date_duration.seconds < 259200): # Second unit
|
||||
score += 100
|
||||
if (published_date_duration.seconds < 259200): # Second unit
|
||||
score += 100
|
||||
elif (published_date_duration.seconds < 604800):
|
||||
score += 75
|
||||
elif (published_date_duration.seconds < 2592000):
|
||||
@ -211,17 +215,20 @@ class Question(models.Model):
|
||||
else:
|
||||
score += 25
|
||||
|
||||
if (up == None) and (down == None):
|
||||
if (up is None) and (down is None):
|
||||
score += ((self.up_vote_count/5) - (self.down_vote_count/5)) * 100
|
||||
else:
|
||||
score += ((up/5) - (down/5)) * 100
|
||||
|
||||
return score
|
||||
|
||||
def get_tags(self, *args, **kwargs):
|
||||
return "-".join([tag.tag_text for tag in self.tags.all()])
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""Modify save method of Question object"""
|
||||
# to-be-added instance # * https://github.com/django/django/blob/866122690dbe233c054d06f6afbc2f3cc6aea2f2/django/db/models/base.py#L447
|
||||
# to-be-added instance
|
||||
# * https://github.com/django/django/blob/866122690dbe233c054d06f6afbc2f3cc6aea2f2/django/db/models/base.py#L447
|
||||
if self._state.adding:
|
||||
try:
|
||||
self.trend_score = self.trending_score()
|
||||
@ -263,7 +270,7 @@ class Vote(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.user} voted for {self.choice} in {self.question}"
|
||||
|
||||
|
||||
|
||||
# ! Most of the code from https://stackoverflow.com/a/70869267
|
||||
class SentimentVote(models.Model):
|
||||
@ -288,4 +295,4 @@ class SentimentVote(models.Model):
|
||||
unique_together (list of str): Ensures that a user can only cast one sentiment vote (upvote or downvote)
|
||||
for a specific question.
|
||||
"""
|
||||
unique_together = ['user', 'question']
|
||||
unique_together = ['user', 'question']
|
||||
|
||||
@ -4,6 +4,7 @@ from django.dispatch import receiver
|
||||
|
||||
log = logging.getLogger("django")
|
||||
|
||||
|
||||
def get_client_ip(request):
|
||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
if x_forwarded_for:
|
||||
@ -12,9 +13,10 @@ def get_client_ip(request):
|
||||
ip = request.META.get('REMOTE_ADDR')
|
||||
return ip
|
||||
|
||||
#! https://stackoverflow.com/questions/37618473/how-can-i-log-both-successful-and-failed-login-and-logout-attempts-in-django
|
||||
|
||||
# !https://stackoverflow.com/questions/37618473/how-can-i-log-both-successful-and-failed-login-and-logout-attempts-in-django
|
||||
@receiver(user_logged_in)
|
||||
def user_logged_in_callback(sender, request, user, **kwargs):
|
||||
def user_logged_in_callback(sender, request, user, **kwargs):
|
||||
ip = get_client_ip(request)
|
||||
|
||||
log.info('Login User: {user} via ip: {ip}'.format(
|
||||
@ -22,8 +24,9 @@ def user_logged_in_callback(sender, request, user, **kwargs):
|
||||
ip=ip
|
||||
))
|
||||
|
||||
|
||||
@receiver(user_logged_out)
|
||||
def user_logged_out_callback(sender, request, user, **kwargs):
|
||||
def user_logged_out_callback(sender, request, user, **kwargs):
|
||||
ip = get_client_ip(request)
|
||||
|
||||
log.info('Logout User: {user} via ip: {ip}'.format(
|
||||
@ -31,8 +34,9 @@ def user_logged_out_callback(sender, request, user, **kwargs):
|
||||
ip=ip
|
||||
))
|
||||
|
||||
|
||||
@receiver(user_login_failed)
|
||||
def user_login_failed_callback(sender, credentials, **kwargs):
|
||||
log.warning('Login Failed for: {credentials}'.format(
|
||||
credentials=credentials,
|
||||
))
|
||||
))
|
||||
|
||||
145
polls/templates/polls/creation.html
Normal file
145
polls/templates/polls/creation.html
Normal file
@ -0,0 +1,145 @@
|
||||
{% extends "polls/base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<header>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/flowbite/1.8.1/flowbite.min.css" rel="stylesheet" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/1.8.1/flowbite.min.js"></script>
|
||||
</header>
|
||||
<div class="min-h-screen bg-gray-100 flex items-center justify-center p-8">
|
||||
<div class="bg-white p-8 shadow-md rounded-md w-full border-solid border-neutral-500 border-2">
|
||||
|
||||
<a href="{% url 'polls:index' %}" class="block text-blue-500 hover:underline mb-4">< Back</a>
|
||||
|
||||
<form method="post" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2" for="{{ form.question_text.id_for_label }}">Question</label>
|
||||
{{ form.question_text }}
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2" for="{{ form.pub_date.id_for_label }}">Publication Date</label>
|
||||
{{ form.pub_date }}
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2" for="{{ form.end_date.id_for_label }}">End Date</label>
|
||||
{{ form.end_date }}
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2" for="{{ form.short_description.id_for_label }}">Short Description</label>
|
||||
{{ form.short_description }}
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2" for="{{ form.long_description.id_for_label }}">Long Description</label>
|
||||
{{ form.long_description }}
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2">Tags(Press Ctrl to select Multiple Tags)</label>
|
||||
{{ form.tags }}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2">Choice(Seperate by comma)</label>
|
||||
{{ form.user_choice }}
|
||||
</div>
|
||||
{% comment %} <div class="mb-4">
|
||||
<label class="block text-gray-700 text-sm font-bold mb-2" for="{{ form.user_choice.id_for_label }}">User Choice</label>
|
||||
<div class="flex space-x-2">
|
||||
<input type="text" id="{{ form.user_choice.id_for_label }}" class="form-input" placeholder="{{ form.user_choice.field.widget.attrs.placeholder }}">
|
||||
<button type="button" id="add-choice-button" class="bg-blue-500 text-white font-bold py-2 px-4 rounded-full hover:bg-blue-700 focus:outline-none focus:shadow-outline-blue active:bg-blue-800">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="user-choices-container" class="mb-4 pb-2">
|
||||
</div> {% endcomment %}
|
||||
|
||||
<hr class="h-px my-4 bg-neutral-0 border-0 dark:bg-neutral-0" />
|
||||
<!--Create Button-->
|
||||
<a data-modal-target="popup-modal" data-modal-toggle="popup-modal" class="bg-blue-500 text-white font-bold py-2 mt-3 px-4 rounded-full hover:bg-blue-700 focus:outline-none focus:shadow-outline-blue active:bg-blue-800 cursor-pointer">Create Poll</a>
|
||||
|
||||
<!--Code from https://flowbite.com/docs/components/modal/-->
|
||||
<div id="popup-modal" tabindex="-1" class="fixed top-0 left-0 right-0 z-50 hidden p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
||||
<div class="relative w-full max-w-md max-h-full">
|
||||
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
|
||||
<button type="button" class="absolute top-3 right-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ml-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white" data-modal-hide="popup-modal">
|
||||
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
||||
</svg>
|
||||
<span class="sr-only">Close modal</span>
|
||||
</button>
|
||||
<div class="p-6 text-center">
|
||||
<svg class="mx-auto mb-4 text-gray-400 w-12 h-12 dark:text-gray-200" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
|
||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 11V6m0 8h.01M19 10a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>
|
||||
</svg>
|
||||
<h3 class="mb-5 text-lg font-normal text-gray-500 dark:text-gray-400">Are you sure to create this poll?</h3>
|
||||
<button data-modal-hide="popup-modal" type="submit" class="text-white bg-green-500 hover:bg-green-800 focus:ring-4 focus:outline-none focus:ring-green-300 dark:focus:ring-red-800 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center mr-2">
|
||||
Yes, I'm sure
|
||||
</button>
|
||||
<button data-modal-hide="popup-modal" type="button" class="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-gray-200 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600">No, cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!--Thank you CHATGPT-->
|
||||
<!--Thank you CHATGPT-->
|
||||
<!--Thank you CHATGPT-->
|
||||
{% comment %} <script>
|
||||
const userChoicesContainer = document.getElementById('user-choices-container');
|
||||
const userInput = document.getElementById('{{ form.user_choice.id_for_label }}');
|
||||
const addButton = document.getElementById('add-choice-button');
|
||||
|
||||
// Function to create a new choice element
|
||||
function createChoiceElement(choiceValue) {
|
||||
const choiceElement = document.createElement('div');
|
||||
choiceElement.textContent = choiceValue;
|
||||
choiceElement.className = 'bg-gray-300 p-2 rounded-md';
|
||||
|
||||
// Add a delete button next to the choice
|
||||
const deleteButton = document.createElement('button');
|
||||
deleteButton.textContent = 'Delete';
|
||||
deleteButton.className = 'bg-red-500 text-white font-bold py-1 px-2 mx-4 rounded-full hover:bg-red-700 focus:outline-none focus:shadow-outline-red active:bg-red-800';
|
||||
deleteButton.addEventListener('click', function () {
|
||||
// Remove the choice element when the delete button is clicked
|
||||
userChoicesContainer.removeChild(choiceElement);
|
||||
});
|
||||
|
||||
choiceElement.appendChild(deleteButton);
|
||||
return choiceElement;
|
||||
}
|
||||
|
||||
addButton.addEventListener('click', function () {
|
||||
const choiceValue = userInput.value.trim();
|
||||
|
||||
if (choiceValue !== '') {
|
||||
// Create a new choice element
|
||||
const choiceElement = createChoiceElement(choiceValue);
|
||||
|
||||
// Append the choice element to the container
|
||||
userChoicesContainer.appendChild(choiceElement);
|
||||
|
||||
// Clear the user input
|
||||
userInput.value = '';
|
||||
}
|
||||
});
|
||||
|
||||
// Handle delete button clicks using event delegation
|
||||
userChoicesContainer.addEventListener('click', function (event) {
|
||||
if (event.target && event.target.tagName === 'BUTTON' && event.target.textContent === 'Delete') {
|
||||
// Remove the choice element when a delete button is clicked
|
||||
userChoicesContainer.removeChild(event.target.parentNode);
|
||||
}
|
||||
});
|
||||
</script> {% endcomment %}
|
||||
|
||||
{% endblock content %}
|
||||
@ -12,14 +12,11 @@
|
||||
</div>
|
||||
<!-- Button -->
|
||||
<div>
|
||||
{% comment %}
|
||||
<button class="mr-4 rounded-md bg-green-500 px-4 py-2 text-white">New Poll</button>
|
||||
{% endcomment %}
|
||||
<div class="flex flex-wrap items-center space-x-2">
|
||||
<header class="flex items-center justify-center">
|
||||
<div class="flex flex-wrap items-center space-x-2">
|
||||
<!--Searchhhh-->
|
||||
<form class="group relative z-30 flex flex-grow flex-col items-center space-y-2 rounded-lg border-2 border-neutral-700 bg-white p-1 pl-2 text-lg md:flex-row md:space-y-0 md:space-x-1 md:rounded-full">
|
||||
<form action="{% url "polls:search_poll" %}" method="get" class="group relative z-30 flex flex-grow flex-col items-center space-y-2 rounded-lg border-2 border-neutral-700 bg-white p-1 pl-2 text-lg md:flex-row md:space-y-0 md:space-x-1 md:rounded-full">
|
||||
<div class="flex w-full items-center rounded-lg bg-neutral-200 py-1 px-2 focus-within:outline-none focus-within:ring-2 focus-within:ring-purple-400 md:rounded-full">
|
||||
<div>🔎</div>
|
||||
<input type="text" name="q" class="font-semibold w-full border-none bg-transparent focus:outline-none focus:ring focus:ring-transparent text-sm">
|
||||
@ -34,9 +31,9 @@
|
||||
</div>
|
||||
</header>
|
||||
{% if user.is_authenticated %}
|
||||
<button class="flex items-center whitespace-nowrap rounded-full border border-transparent bg-green-500 px-5 py-2 text-sm font-bold text-white transition duration-150 ease-in-out hover:scale-[101%] hover:bg-green-700 focus:bg-green-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 active:bg-green-900">
|
||||
<a href="{% url "polls:create_poll" %}" class="flex items-center whitespace-nowrap rounded-full border border-transparent bg-green-500 px-5 py-2 text-sm font-bold text-white transition duration-150 ease-in-out hover:scale-[101%] hover:bg-green-700 focus:bg-green-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 active:bg-green-900">
|
||||
New Poll
|
||||
</button>
|
||||
</a>
|
||||
<a href="{% url 'logout' %}"
|
||||
class="flex items-center whitespace-nowrap rounded-full border border-transparent bg-red-600 px-5 py-2 text-sm font-bold text-white transition duration-150 ease-in-out hover:scale-[101%] hover:bg-red-700 focus:bg-neutral-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 active:bg-neutral-900">
|
||||
<span>Sign out <span class="hidden sm:inline-block">😭</span></span>
|
||||
@ -90,6 +87,7 @@
|
||||
<h2 class="mb-2 text-xl font-bold truncate">{{ question.question_text }}</h2>
|
||||
<hr class="h-px my-2 bg-gray-200 border-0 dark:bg-gray-400" />
|
||||
<p class="mb-2 text-gray-600">{{ question.short_description }}</p>
|
||||
<!--Up, Down Vote-->
|
||||
<div class="mb-2 flex items-center text-gray-600">
|
||||
<span class="mr-2">👍</span>
|
||||
<span>{{ question.up_vote_percentage }}% Upvoted</span>
|
||||
@ -97,12 +95,20 @@
|
||||
<span class="ml-4 mr-2">👎</span>
|
||||
<span>{{ question.down_vote_percentage }}% Downvoted</span>
|
||||
</div>
|
||||
<!-- Tag / Time -->
|
||||
<!-- Participant, Time -->
|
||||
<div class="flex items-center text-gray-600">
|
||||
<span class="mr-2 rounded-md bg-green-500 px-2 py-1 text-white">🕒 {{ question.time_left }}</span>
|
||||
<span class="mr-2 rounded-md bg-orange-100 px-2 py-1 text-black">{{ question.participants }} Participants 👤</span>
|
||||
</div>
|
||||
<div class="flex items-center text-gray-600 py-4">
|
||||
<!-- Tags-->
|
||||
<div class="flex items-center text-gray-600 pt-2">
|
||||
{% for tag in question.tags.all %}
|
||||
<span class="mr-2 rounded-md bg-blue-100 px-1 py-1 text-blue-400 text-xs text-black font-bold">{{ tag.tag_text }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<hr class="h-px my-4 bg-gray-200 border-0 dark:bg-gray-400" />
|
||||
<!--Vote View Button-->
|
||||
<div class="flex items-center text-gray-600">
|
||||
<button
|
||||
onclick="window.location.href='{% url 'polls:detail' question.id %}'"
|
||||
class="mr-2 rounded-md bg-yellow-100 px-2 py-1 text-black font-semibold border-solid border-2 border-yellow-500 hover:bg-yellow-500 transform translate-y-0 hover:translate-y-1 transition-transform">
|
||||
@ -136,6 +142,7 @@
|
||||
<h2 class="mb-2 text-xl font-semibold truncate">{{ question.question_text }}</h2>
|
||||
<hr class="h-px my-2 bg-gray-200 border-0 dark:bg-gray-400" />
|
||||
<p class="mb-2 text-gray-600">{{ question.short_description }}</p>
|
||||
<!--Up, Down Vote-->
|
||||
<div class="mb-2 flex items-center text-gray-600">
|
||||
<span class="mr-2">👍</span>
|
||||
<span>{{ question.up_vote_percentage }}% Upvoted</span>
|
||||
@ -143,12 +150,20 @@
|
||||
<span class="ml-4 mr-2">👎</span>
|
||||
<span>{{ question.down_vote_percentage }}% Downvoted</span>
|
||||
</div>
|
||||
<!-- Tag / Time -->
|
||||
<!-- Participant, Time -->
|
||||
<div class="flex items-center text-gray-600">
|
||||
<span class="mr-2 rounded-md bg-green-500 px-2 py-1 text-white">🕒 {{ question.time_left }}</span>
|
||||
<span class="mr-2 rounded-md bg-orange-100 px-2 py-1 text-black">{{ question.participants }} Participants 👤</span>
|
||||
</div>
|
||||
<div class="flex items-center text-gray-600 py-4">
|
||||
<!-- Tags-->
|
||||
<div class="flex pt-2">
|
||||
{% for tag in question.tags.all %}
|
||||
<span class="mr-2 rounded-md bg-blue-100 px-1 py-1 text-blue-400 text-xs text-black font-bold">{{ tag.tag_text }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<hr class="h-px my-4 bg-gray-200 border-0 dark:bg-gray-400" />
|
||||
<!--Vote View Button-->
|
||||
<div class="flex items-center text-gray-600">
|
||||
<button
|
||||
onclick="window.location.href='{% url 'polls:detail' question.id %}'"
|
||||
class="mr-2 rounded-md bg-white px-2 py-1 text-black border-solid border-2 border-black hover:bg-gray-500 transform translate-y-0 hover:translate-y-1 transition-transform">
|
||||
|
||||
110
polls/templates/polls/search.html
Normal file
110
polls/templates/polls/search.html
Normal file
@ -0,0 +1,110 @@
|
||||
{% extends "polls/base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Navbar -->
|
||||
<nav class="bg-blue-500 p-4 shadow-xl border-b-2 border-solid border-neutral-700">
|
||||
<div class="container mx-auto flex items-center justify-between">
|
||||
<div class="text-2xl font-bold text-white">
|
||||
{% if user.is_authenticated %}
|
||||
<a href={% url "polls:index" %}>📝BACK TO KU POLL</a> | 👋 Hi! {{ user.username }}
|
||||
{% else %}
|
||||
<a href={% url "polls:index" %}>📝BACK TO KU POLL</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<!-- Button -->
|
||||
<div>
|
||||
{% comment %}
|
||||
<button class="mr-4 rounded-md bg-green-500 px-4 py-2 text-white">New Poll</button>
|
||||
{% endcomment %}
|
||||
<div class="flex flex-wrap items-center space-x-2">
|
||||
<header class="flex items-center justify-center">
|
||||
<div class="flex flex-wrap items-center space-x-2">
|
||||
<!--Searchhhh-->
|
||||
<form action="{% url "polls:search_poll" %}" method="get" class="group relative z-30 flex flex-grow flex-col items-center space-y-2 rounded-lg border-2 border-neutral-700 bg-white p-1 pl-2 text-lg md:flex-row md:space-y-0 md:space-x-1 md:rounded-full">
|
||||
<div class="flex w-full items-center rounded-lg bg-neutral-200 py-1 px-2 focus-within:outline-none focus-within:ring-2 focus-within:ring-purple-400 md:rounded-full">
|
||||
<div>🔎</div>
|
||||
<input type="text" name="q" class="font-semibold w-full border-none bg-transparent focus:outline-none focus:ring focus:ring-transparent text-sm">
|
||||
</div>
|
||||
<button type="submit">
|
||||
<span class="flex items-center whitespace-nowrap rounded-full border border-transparent bg-green-500 px-5 py-2 text-sm font-bold text-white transition duration-150 ease-in-out hover:scale-[101%] hover:bg-green-700 focus:bg-green-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 active:bg-green-900">
|
||||
Search
|
||||
</span>
|
||||
</button>
|
||||
</form>
|
||||
<!--End-->
|
||||
</div>
|
||||
</header>
|
||||
{% if user.is_authenticated %}
|
||||
<a href="{% url "polls:create_poll" %}" class="flex items-center whitespace-nowrap rounded-full border border-transparent bg-green-500 px-5 py-2 text-sm font-bold text-white transition duration-150 ease-in-out hover:scale-[101%] hover:bg-green-700 focus:bg-green-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 active:bg-green-900">
|
||||
New Poll
|
||||
</a>
|
||||
<a href="{% url 'logout' %}"
|
||||
class="flex items-center whitespace-nowrap rounded-full border border-transparent bg-red-600 px-5 py-2 text-sm font-bold text-white transition duration-150 ease-in-out hover:scale-[101%] hover:bg-red-700 focus:bg-neutral-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 active:bg-neutral-900">
|
||||
<span>Sign out <span class="hidden sm:inline-block">😭</span></span>
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{% url 'login' %}"
|
||||
class="flex items-center whitespace-nowrap rounded-full border border-transparent bg-neutral-800 px-5 py-2 text-sm font-bold text-white transition duration-150 ease-in-out hover:scale-[101%] hover:bg-neutral-700 focus:bg-neutral-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 active:bg-red-900">
|
||||
<span>Sign in <span class="hidden sm:inline-block">😎</span></span>
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="bg-white p-4 rounded-lg shadow-md mb-4">
|
||||
{% if q %}
|
||||
{% with results.count as total_result %}
|
||||
<h2 class="mb-4 text-2xl font-bold">Found {{ total_result }} Polls!</h2>
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
<hr class="h-px my-4 bg-gray-200 border-0 dark:bg-gray-400" />
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3">
|
||||
{% for question in results %}
|
||||
<div class="relative">
|
||||
<!-- INFO -->
|
||||
<div class="rounded-lg bg-white p-4 shadow-md border-solid border-2 border-neutral-500 relative z-10 transform translate-y-0 hover:translate-y-1 transition-transform">
|
||||
<h2 class="mb-2 text-xl font-semibold truncate">{{ question.question_text }}</h2>
|
||||
<hr class="h-px my-2 bg-gray-200 border-0 dark:bg-gray-400" />
|
||||
<p class="mb-2 text-gray-600">{{ question.short_description }}</p>
|
||||
<div class="mb-2 flex items-center text-gray-600">
|
||||
<span class="mr-2">👍</span>
|
||||
<span>{{ question.up_vote_percentage }}% Upvoted</span>
|
||||
|
||||
<span class="ml-4 mr-2">👎</span>
|
||||
<span>{{ question.down_vote_percentage }}% Downvoted</span>
|
||||
</div>
|
||||
<!-- Tag / Time -->
|
||||
<div class="flex items-center text-gray-600">
|
||||
<span class="mr-2 rounded-md bg-green-500 px-2 py-1 text-white">🕒 {{ question.time_left }}</span>
|
||||
<span class="mr-2 rounded-md bg-orange-100 px-2 py-1 text-black">{{ question.participants }} Participants 👤</span>
|
||||
</div>
|
||||
<div class="flex items-center text-gray-600 py-4">
|
||||
<button
|
||||
onclick="window.location.href='{% url 'polls:detail' question.id %}'"
|
||||
class="mr-2 rounded-md bg-white px-2 py-1 text-black border-solid border-2 border-black hover:bg-gray-500 transform translate-y-0 hover:translate-y-1 transition-transform">
|
||||
VOTE
|
||||
</button>
|
||||
<button
|
||||
onclick="window.location.href='{% url 'polls:results' question.id %}'"
|
||||
class="mr-2 rounded-md bg-white px-2 py-1 text-black border-solid border-2 border-black hover:bg-gray-500 transform translate-y-0 hover:translate-y-1 transition-transform">
|
||||
VIEW
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% if forloop.counter|divisibleby:2 %}
|
||||
<div class="absolute inset-0 mt-1 ml-1 h-full w-full rounded-lg border-2 border-neutral-700 bg-gradient-to-r from-green-400 to-blue-500">
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="absolute inset-0 mt-1 ml-1 h-full w-full rounded-lg border-2 border-neutral-700 bg-gradient-to-r from-orange-400 to-red-500">
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
@ -1,7 +1,6 @@
|
||||
from django.utils import timezone
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from ..models import Question, Vote, Choice
|
||||
from ..models import Question
|
||||
|
||||
|
||||
def create_question(question_text, day=0):
|
||||
@ -11,4 +10,4 @@ def create_question(question_text, day=0):
|
||||
in the past, positive for questions that have yet to be published).
|
||||
"""
|
||||
time = timezone.now() + timezone.timedelta(days=day)
|
||||
return Question.objects.create(question_text=question_text, pub_date=time)
|
||||
return Question.objects.create(question_text=question_text, pub_date=time)
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
from django.test import TestCase, Client
|
||||
from django.utils import timezone
|
||||
from django.urls import reverse
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
@ -102,4 +102,4 @@ class QuestionModelTests(TestCase):
|
||||
pub_date = timezone.now() - datetime.timedelta(hours=1)
|
||||
end_date = timezone.now() + datetime.timedelta(hours=2)
|
||||
question = Question(pub_date=pub_date, end_date=end_date)
|
||||
self.assertIs(question.can_vote(), True)
|
||||
self.assertIs(question.can_vote(), True)
|
||||
|
||||
23
polls/tests/test_search_poll.py
Normal file
23
polls/tests/test_search_poll.py
Normal file
@ -0,0 +1,23 @@
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from ..models import Question
|
||||
|
||||
|
||||
class SearchPollTest(TestCase):
|
||||
"""Test if user search with normal string. It must return same queryset as filter question objects"""
|
||||
def test_search_normal_poll(self):
|
||||
data_1 = {'q': 'what'}
|
||||
data_2 = {'q': 'prefer'}
|
||||
q_1 = 'what'
|
||||
q_2 = 'prefer'
|
||||
response_1 = self.client.get(reverse("polls:search_poll"), data_1)
|
||||
response_2 = self.client.get(reverse("polls:search_poll"), data_2)
|
||||
self.assertQuerysetEqual(response_1.context['results'], Question.objects.filter(question_text__icontains=q_1))
|
||||
self.assertQuerysetEqual(response_2.context['results'], Question.objects.filter(question_text__icontains=q_2))
|
||||
|
||||
def test_search_with_empty(self):
|
||||
"""Test if user search with empty string. It must return all question"""
|
||||
data = {'q': ''}
|
||||
response = self.client.get(reverse("polls:search_poll"), data)
|
||||
self.assertQuerysetEqual(response.context['results'], Question.objects.all())
|
||||
@ -2,7 +2,7 @@ from django.test import TransactionTestCase, Client
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from .base import create_question
|
||||
from ..views import up_down_vote
|
||||
|
||||
|
||||
# ! https://stackoverflow.com/questions/24588520/testing-several-integrityerrors-in-the-same-django-unittest-test-case
|
||||
# * https://stackoverflow.com/questions/44450533/difference-between-testcase-and-transactiontestcase-classes-in-django-test
|
||||
@ -32,11 +32,11 @@ class UpDownVoteViewTest(TransactionTestCase):
|
||||
self.assertEqual(count_up, 0)
|
||||
self.assertEqual(count_down, 1)
|
||||
|
||||
def test_can_change_up_to_down(self):
|
||||
def test_can_change_down_to_up(self):
|
||||
self.client.login(username="test_user", password="12345abc")
|
||||
self.q1.downvote(self.user)
|
||||
self.q1.upvote(self.user)
|
||||
count_up = self.q1.sentimentvote_set.filter(vote_types=True).count()
|
||||
count_down = self.q1.sentimentvote_set.filter(vote_types=False).count()
|
||||
self.assertEqual(count_up, 1)
|
||||
self.assertEqual(count_down, 0)
|
||||
self.assertEqual(count_down, 0)
|
||||
|
||||
@ -42,4 +42,4 @@ class SignUpTestCase(TestCase):
|
||||
'password2': 'testpassword123',
|
||||
}
|
||||
response = self.client.post(signup_url, data)
|
||||
self.assertRedirects(response, reverse("polls:index"))
|
||||
self.assertRedirects(response, reverse("polls:index"))
|
||||
|
||||
@ -5,6 +5,7 @@ from django.contrib.auth.models import User
|
||||
from .base import create_question
|
||||
from ..models import Vote, Choice
|
||||
|
||||
|
||||
class VoteViewTest(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
@ -20,9 +21,9 @@ class VoteViewTest(TestCase):
|
||||
"""
|
||||
self.client.login(username=self.user.username, password="aaa123321aaa")
|
||||
|
||||
response = self.client.post(reverse("polls:vote", args=(self.question.id,)),
|
||||
{'choice' : self.choice1.id})
|
||||
|
||||
response = self.client.post(reverse("polls:vote", args=(self.question.id,)),
|
||||
{'choice': self.choice1.id})
|
||||
|
||||
self.assertRedirects(response, reverse("polls:results", args=(self.question.id,)))
|
||||
self.assertTrue(Vote.objects.filter(user=self.user, question=self.question).exists())
|
||||
|
||||
@ -32,18 +33,18 @@ class VoteViewTest(TestCase):
|
||||
"""
|
||||
self.client.login(username=self.user.username, password="aaa123321aaa")
|
||||
|
||||
response = self.client.post(reverse("polls:vote", args=(self.question.id,)),
|
||||
{'choice' : 1000})
|
||||
|
||||
response = self.client.post(reverse("polls:vote", args=(self.question.id,)),
|
||||
{'choice': 1000})
|
||||
|
||||
self.assertRedirects(response, reverse('polls:detail', args=(self.question.id,)))
|
||||
|
||||
def test_vote_without_login(self):
|
||||
"""
|
||||
Test the vote view when the user is not logged in.
|
||||
"""
|
||||
response = self.client.post(reverse("polls:vote", args=(self.question.id,)),
|
||||
{'choice' : self.choice1})
|
||||
|
||||
response = self.client.post(reverse("polls:vote", args=(self.question.id,)),
|
||||
{'choice': self.choice1})
|
||||
|
||||
self.assertRedirects(response, "/accounts/login/?next=/polls/1/vote/")
|
||||
|
||||
def test_vote_voting_not_allowed(self):
|
||||
@ -55,9 +56,8 @@ class VoteViewTest(TestCase):
|
||||
self.question_2 = create_question(question_text="Test not allow", day=10)
|
||||
self.choice_2 = Choice.objects.create(question=self.question_2, choice_text="Test Choice 2_2")
|
||||
|
||||
response = self.client.post(reverse("polls:vote", args=(self.question_2.id,)),
|
||||
{"choice" : self.choice_2.id})
|
||||
|
||||
response = self.client.post(reverse("polls:vote", args=(self.question_2.id,)), {"choice": self.choice_2.id})
|
||||
|
||||
self.assertRedirects(response, reverse('polls:index'))
|
||||
|
||||
def test_vote_with_no_post_data(self):
|
||||
@ -81,6 +81,6 @@ class VoteViewTest(TestCase):
|
||||
|
||||
response_2 = self.client.post(reverse("polls:vote", args=(self.question.id,)), {"choice": self.choice2.id})
|
||||
self.assertRedirects(response_2, reverse('polls:results', args=(self.question.id,)))
|
||||
|
||||
|
||||
self.assertFalse(Vote.objects.filter(user=self.user, question=self.question, choice=self.choice1).exists())
|
||||
self.assertTrue(Vote.objects.filter(user=self.user, question=self.question, choice=self.choice2).exists())
|
||||
self.assertTrue(Vote.objects.filter(user=self.user, question=self.question, choice=self.choice2).exists())
|
||||
|
||||
@ -9,6 +9,8 @@ urlpatterns = [
|
||||
path("<int:pk>/results/", views.ResultsView.as_view(), name="results"),
|
||||
path("<int:question_id>/vote/", views.vote, name="vote"),
|
||||
path("signup/", views.SignUpView.as_view(), name="signup"),
|
||||
path("upvote/<int:question_id>", views.up_down_vote, {'vote_type' : 'upvote'}, name="upvote"),
|
||||
path("downvote/<int:question_id>", views.up_down_vote, {'vote_type' : 'downvote'}, name="downvote"),
|
||||
path("upvote/<int:question_id>", views.up_down_vote, {'vote_type': 'upvote'}, name="upvote"),
|
||||
path("downvote/<int:question_id>", views.up_down_vote, {'vote_type': 'downvote'}, name="downvote"),
|
||||
path("search", views.search_poll, name="search_poll"),
|
||||
path("create", views.create_poll, name="create_poll")
|
||||
]
|
||||
|
||||
112
polls/views.py
112
polls/views.py
@ -2,7 +2,6 @@ import logging
|
||||
from typing import Any
|
||||
|
||||
from django.shortcuts import get_object_or_404, render, redirect
|
||||
from django.urls import reverse
|
||||
from django.views import generic
|
||||
from django.utils import timezone
|
||||
from django.urls import reverse_lazy, reverse
|
||||
@ -12,7 +11,7 @@ from django.contrib.auth import authenticate, login
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.db.models import Q
|
||||
|
||||
from .forms import SignUpForm
|
||||
from .forms import SignUpForm, PollSearchForm, PollCreateForm
|
||||
from .models import Choice, Question, Vote
|
||||
|
||||
|
||||
@ -26,21 +25,21 @@ class IndexView(generic.ListView):
|
||||
context_object_name = "latest_question_list"
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
Return the last published questions that is published and haven't ended yet.
|
||||
"""
|
||||
now = timezone.now()
|
||||
all_poll_queryset = Question.objects.filter(
|
||||
Q(pub_date__lte=now) & ((Q(end_date__gte=now) | Q(end_date=None)))
|
||||
).order_by("-pub_date")
|
||||
"""
|
||||
Return the last published questions that is published and haven't ended yet.
|
||||
"""
|
||||
now = timezone.now()
|
||||
all_poll_queryset = Question.objects.filter(
|
||||
Q(pub_date__lte=now) & ((Q(end_date__gte=now) | Q(end_date=None)))
|
||||
).order_by("-pub_date")
|
||||
|
||||
trend_poll_queryset = Question.objects.filter(
|
||||
Q(pub_date__lte=now) & ((Q(end_date__gte=now) | Q(end_date=None))) & Q(trend_score__gte=100)
|
||||
).order_by("trend_score", "end_date")[:3]
|
||||
trend_poll_queryset = Question.objects.filter(
|
||||
Q(pub_date__lte=now) & ((Q(end_date__gte=now) | Q(end_date=None))) & Q(trend_score__gte=100)
|
||||
).order_by("trend_score")[:3]
|
||||
|
||||
queryset = {'all_poll' : all_poll_queryset,
|
||||
'trend_poll' : trend_poll_queryset,}
|
||||
return queryset
|
||||
queryset = {'all_poll': all_poll_queryset,
|
||||
'trend_poll': trend_poll_queryset, }
|
||||
return queryset
|
||||
|
||||
|
||||
class DetailView(LoginRequiredMixin, generic.DetailView):
|
||||
@ -93,12 +92,16 @@ class DetailView(LoginRequiredMixin, generic.DetailView):
|
||||
|
||||
|
||||
class ResultsView(LoginRequiredMixin, generic.DetailView):
|
||||
"""
|
||||
Provide a view for Result page, a Result for the poll contain poll participants
|
||||
number and other statistic such as up, down vote
|
||||
"""
|
||||
model = Question
|
||||
template_name = "polls/results.html"
|
||||
|
||||
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
user_voted = None
|
||||
question = self.get_object()
|
||||
if question.sentimentvote_set.filter(user=self.request.user, question=question, vote_types=True).exists():
|
||||
@ -109,7 +112,11 @@ class ResultsView(LoginRequiredMixin, generic.DetailView):
|
||||
context['user_voted'] = user_voted
|
||||
return context
|
||||
|
||||
|
||||
class SignUpView(generic.CreateView):
|
||||
"""
|
||||
View that responsible for Sign Up page.
|
||||
"""
|
||||
form_class = SignUpForm
|
||||
success_url = reverse_lazy('polls:index')
|
||||
template_name = 'registration/signup.html'
|
||||
@ -146,7 +153,7 @@ def vote(request, question_id):
|
||||
vote, created = Vote.objects.update_or_create(
|
||||
user=request.user,
|
||||
question=question,
|
||||
defaults={'choice' : selected_choice}
|
||||
defaults={'choice': selected_choice}
|
||||
)
|
||||
|
||||
if created:
|
||||
@ -163,13 +170,15 @@ def vote(request, question_id):
|
||||
else:
|
||||
messages.error(request, "Invalid request method.")
|
||||
return redirect("polls:index")
|
||||
|
||||
|
||||
|
||||
@login_required
|
||||
def up_down_vote(request, question_id, vote_type):
|
||||
ip = get_client_ip(request)
|
||||
"""
|
||||
A function that control the upvote and downvote request.
|
||||
"""
|
||||
question = get_object_or_404(Question, pk=question_id)
|
||||
|
||||
|
||||
if request.method == "POST":
|
||||
if vote_type == "upvote":
|
||||
if question.upvote(request.user):
|
||||
@ -177,15 +186,76 @@ def up_down_vote(request, question_id, vote_type):
|
||||
elif vote_type == "downvote":
|
||||
if question.downvote(request.user):
|
||||
messages.success(request, "You downvoted this Poll😭")
|
||||
|
||||
|
||||
return redirect(reverse("polls:results", args=(question_id,)))
|
||||
|
||||
|
||||
# https://stackoverflow.com/questions/4581789/how-do-i-get-user-ip-address-in-django
|
||||
def get_client_ip(request):
|
||||
"""
|
||||
Use with logger to get ip of user.
|
||||
"""
|
||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
if x_forwarded_for:
|
||||
ip = x_forwarded_for.split(',')[0]
|
||||
else:
|
||||
ip = request.META.get('REMOTE_ADDR')
|
||||
return ip
|
||||
|
||||
|
||||
def search_poll(request):
|
||||
"""
|
||||
A function that handle the rendering of search result after user search with
|
||||
search bar.
|
||||
"""
|
||||
form = PollSearchForm
|
||||
|
||||
results = []
|
||||
q = ''
|
||||
if 'q' in request.GET:
|
||||
form = PollSearchForm(request.GET)
|
||||
if form.is_valid():
|
||||
q = form.cleaned_data['q']
|
||||
# Case insensitive (icontains)
|
||||
results = Question.objects.filter(question_text__icontains=q)
|
||||
# * If user search with empty string then show every poll.
|
||||
if q == '':
|
||||
results = Question.objects.all()
|
||||
return render(request, 'polls/search.html', {'form': form, 'results': results, 'q': q})
|
||||
|
||||
|
||||
@login_required
|
||||
def create_poll(request):
|
||||
ip = get_client_ip(request)
|
||||
if request.method == 'POST':
|
||||
form = PollCreateForm(request.POST)
|
||||
if form.is_valid():
|
||||
question_text = form.cleaned_data['question_text']
|
||||
pub_date = form.cleaned_data['pub_date']
|
||||
end_date = form.cleaned_data['end_date']
|
||||
short_description = form.cleaned_data['short_description']
|
||||
long_description = form.cleaned_data.get('long_description', '')
|
||||
user_choices = form.cleaned_data['user_choice']
|
||||
tags = form.cleaned_data['tags']
|
||||
|
||||
question = Question.objects.create(
|
||||
question_text=question_text,
|
||||
pub_date=pub_date,
|
||||
end_date=end_date,
|
||||
short_description=short_description,
|
||||
long_description=long_description,
|
||||
)
|
||||
|
||||
choices = user_choices.split(',') # Split with comma
|
||||
for choice_text in choices:
|
||||
Choice.objects.create(question=question, choice_text=choice_text.strip())
|
||||
|
||||
# Add tags to the question
|
||||
question.tags.set(tags)
|
||||
logger.info(f"User {request.user.username} ({ip}) create poll : {question_text}")
|
||||
return redirect('polls:index')
|
||||
|
||||
else:
|
||||
form = PollCreateForm()
|
||||
|
||||
return render(request, 'polls/creation.html', {'form': form})
|
||||
|
||||
@ -62,4 +62,5 @@ Write-Host $bottomBorder
|
||||
python manage.py migrate
|
||||
python manage.py loaddata data/users.json
|
||||
python manage.py loaddata data/polls.json
|
||||
python manage.py loaddata data/vote.json
|
||||
python manage.py runserver --insecure
|
||||
21
setup.py
21
setup.py
@ -17,7 +17,7 @@ def check_python_command():
|
||||
return command
|
||||
except FileNotFoundError:
|
||||
continue
|
||||
|
||||
|
||||
return None
|
||||
|
||||
def create_virtual_environment(env_name, python_command):
|
||||
@ -31,7 +31,7 @@ def customize_virtual_environment():
|
||||
|
||||
def setup_environment_variables(python_command_in_venv):
|
||||
print("Setting up Django environment variables:")
|
||||
|
||||
|
||||
# SECRET KEY
|
||||
generate_secret_key = input("Generate a Django SECRET_KEY? (yes/no): ").strip().lower()
|
||||
if generate_secret_key == "yes":
|
||||
@ -39,7 +39,7 @@ def setup_environment_variables(python_command_in_venv):
|
||||
'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())']).decode().strip()
|
||||
else:
|
||||
secret_key = input("Enter Django SECRET_KEY: ").strip()
|
||||
|
||||
|
||||
# DEBUG MODE
|
||||
while True:
|
||||
debug_mode = input("Enable DEBUG mode? (True/False): ").strip()
|
||||
@ -52,7 +52,7 @@ def setup_environment_variables(python_command_in_venv):
|
||||
allowed_hosts = input("Enter ALLOWED_HOSTS (comma-separated, or press Enter for default): ").strip()
|
||||
if not allowed_hosts:
|
||||
allowed_hosts = "*.ku.th,localhost,127.0.0.1,::1"
|
||||
|
||||
|
||||
# TZ
|
||||
available_time_zones = ["Asia/Bangkok", "Japan", "UCT", "CST6CDT", "Custom"]
|
||||
|
||||
@ -73,7 +73,7 @@ def setup_environment_variables(python_command_in_venv):
|
||||
print("Invalid choice. Please enter a valid number.")
|
||||
except ValueError:
|
||||
print("Invalid input. Please enter a valid number.")
|
||||
|
||||
|
||||
email_host_password = input("Enter EMAIL_HOST_PASSWORD: ").strip()
|
||||
|
||||
# SET
|
||||
@ -116,7 +116,7 @@ def main():
|
||||
elif is_windows:
|
||||
activate_command = os.path.join(".venv", "Scripts", "activate")
|
||||
subprocess.run([activate_command], shell=True)
|
||||
|
||||
|
||||
python_command = os.path.join(".venv", "bin", "python") if is_posix else os.path.join(".venv", "Scripts", "python")
|
||||
else:
|
||||
print("Not setting up a virtual environment. Using the global Python interpreter.")
|
||||
@ -136,6 +136,7 @@ def main():
|
||||
subprocess.run([python_command, "manage.py", "migrate"])
|
||||
subprocess.run([python_command, "manage.py", "loaddata", "data/users.json"])
|
||||
subprocess.run([python_command, "manage.py", "loaddata", "data/polls.json"])
|
||||
subprocess.run([python_command, "manage.py", "loaddata", "data/vote.json"])
|
||||
|
||||
start_server = input("Do you want to start the Django server? (yes/no): ").lower()
|
||||
if start_server == "yes":
|
||||
@ -154,19 +155,20 @@ def main():
|
||||
print(f"==========================Install Requirement==========================")
|
||||
subprocess.run([python_command_in_venv, "-m", "pip", "install", "-r", "requirements.txt"])
|
||||
setup_environment_variables(python_command_in_venv)
|
||||
|
||||
|
||||
subprocess.run([python_command_in_venv, "-m", "pip", "install", "-r", "requirements.txt"], check=True)
|
||||
subprocess.run([python_command_in_venv, "manage.py", "migrate"], check=True)
|
||||
subprocess.run([python_command_in_venv, "manage.py", "loaddata", "data/users.json"], check=True)
|
||||
subprocess.run([python_command_in_venv, "manage.py", "loaddata", "data/polls.json"], check=True)
|
||||
|
||||
subprocess.run([python_command_in_venv, "manage.py", "loaddata", "data/vote.json"], check=True)
|
||||
|
||||
start_server = input("Do you want to start the Django server? (yes/no): ").strip().lower()
|
||||
if start_server == "yes":
|
||||
print("=================================================")
|
||||
print("Django run in --insecure mode to load Static File")
|
||||
print("==================================================")
|
||||
subprocess.run([python_command_in_venv, "manage.py", "runserver", "--insecure"], check=True)
|
||||
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error: {e}")
|
||||
sys.exit(1)
|
||||
@ -174,5 +176,6 @@ def main():
|
||||
print("\nSetup process aborted.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user