-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Реліз
- Loading branch information
Showing
115 changed files
with
3,332 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
PROJECT_NAME=binova-personal-assistant | ||
|
||
DJANGO_SECRET= | ||
DJANGO_ALLOWED_HOSTS= | ||
DJANGO_DEBUG= | ||
|
||
DJANGO_EMAIL_HOST= | ||
DJANGO_EMAIL_PORT= | ||
DJANGO_EMAIL_USER= | ||
DJANGO_EMAIL_PASSWORD= | ||
|
||
DATABASE_TAG=16.4-alpine3.20 | ||
DATABASE_HOST= | ||
DATABASE_PORT= | ||
DATABASE_ENGINE= | ||
DATABASE_OPTIONS= | ||
DATABASE_NAME= | ||
DATABASE_USER= | ||
DATABASE_PASSWORD= | ||
|
||
CLOUDINARY_CLOUD_NAME= | ||
CLOUDINARY_API_KEY= | ||
CLOUDINARY_API_SECRET= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.env | ||
.venv | ||
poetry.lock | ||
db.sqlite3 | ||
__pycache__ | ||
personal_assistant/static |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,69 @@ | ||
``` | ||
____ _ _ _ _ _ | ||
| _ \ ___ _ __ ___ ___ _ __ __ _| | / \ ___ ___(_)___| |_ __ _ _ __ | |_ | ||
| |_) / _ \ '__/ __|/ _ \| '_ \ / _` | | / _ \ / __/ __| / __| __/ _` | '_ \| __| | ||
| __/ __/ | \__ \ (_) | | | | (_| | | / ___ \\__ \__ \ \__ \ || (_| | | | | |_ | ||
|_| \___|_| |___/\___/|_| |_|\__,_|_| /_/ \_\___/___/_|___/\__\__,_|_| |_|\__| | ||
``` | ||
|
||
# Personal Assistant | ||
|
||
This web application offers a comprehensive suite of features for managing | ||
contacts, notes, and files, all integrated with a powerful tagging system for | ||
enhanced organization and search capabilities. It also provides up-to-date news | ||
and currency exchange rates, ensuring users have access to real-time | ||
information. With robust user authentication and personalized data access, the | ||
application ensures a secure and customized experience, while utilizing cloud | ||
storage and caching for optimal performance. | ||
|
||
|
||
## Features | ||
|
||
- **Contacts:** Allows users to manage their contact list with CRUD operations, | ||
including organizing by birthdays and searching by name. | ||
|
||
- **Tags:** Enables users to create and delete tags, with restrictions on | ||
deletion if the tag is in use, and shared across all users. | ||
|
||
- **Notes:** Provides a simple note-taking feature with title and description, | ||
enhanced by tag-based categorization and advanced search functionality. | ||
|
||
- **Files:** Allows file uploads and viewing, securely storing content in the | ||
cloud and enabling filtering and sorting via a tag-like system. | ||
|
||
- **News:** Displays global news and currency exchange rates, with real-time | ||
data and a caching system for faster load times, including a | ||
superuser-controlled data sync. | ||
|
||
- **Users:** Manages user authentication, including registration, login, and | ||
password recovery, ensuring personalized access to data and features while | ||
protecting against spam accounts. | ||
|
||
|
||
## Installation | ||
|
||
**Note:** The Docker command is optional since the project can work with | ||
`SQLite` when environment variables for `PostgreSQL` are not defined. | ||
|
||
```bash | ||
$ git clone https://github.com/BIN0VA/Personal-Assistant.git | ||
$ cd Personal-Assistant | ||
$ docker compose up -d | ||
$ poetry shell | ||
$ poetry install | ||
$ cd personal_assistant | ||
$ python manage.py migrate | ||
$ python manage.py createsuperuser | ||
``` | ||
|
||
|
||
## Usage | ||
|
||
```bash | ||
$ docker compose up -d | ||
$ poetry shell | ||
$ cd personal_assistant | ||
$ python manage.py runserver | ||
``` | ||
|
||
Go to http://localhost:8000. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
services: | ||
db: | ||
image: "postgres:${DATABASE_TAG}" | ||
container_name: "${PROJECT_NAME}-database" | ||
environment: | ||
POSTGRES_DB: $DATABASE_NAME | ||
POSTGRES_USER: $DATABASE_USER | ||
POSTGRES_PASSWORD: $DATABASE_PASSWORD | ||
ports: | ||
- "${DATABASE_PORT}:5432" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
#!/usr/bin/env python | ||
"""Django's command-line utility for administrative tasks.""" | ||
from os import environ | ||
from sys import argv | ||
|
||
|
||
def main() -> None: | ||
"""Run administrative tasks.""" | ||
environ.setdefault('DJANGO_SETTINGS_MODULE', 'personal_assistant.settings') | ||
|
||
try: | ||
from django.core.management import execute_from_command_line | ||
except ImportError as exc: | ||
raise ImportError( | ||
"Couldn't import Django. Are you sure it's installed and " | ||
"available on your PYTHONPATH environment variable? Did you " | ||
"forget to activate a virtual environment?" | ||
) from exc | ||
|
||
execute_from_command_line(argv) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from django.contrib import admin | ||
from .models import Contact | ||
|
||
|
||
admin.site.register(Contact) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class ContactsConfig(AppConfig): | ||
default_auto_field = 'django.db.models.BigAutoField' | ||
name = 'pa_contacts' | ||
verbose_name = 'Contacts' | ||
description = '''Allows users to manage their contact list with CRUD operations, including organizing by birthdays and searching by name. | ||
This web application is designed to manage contacts and perform full CRUD (Create, Read, Update, Delete) operations on them. Each contact entry contains several fields: name, physical address, email address, phone number, and date of birth. A key feature of the application is a dedicated section that displays upcoming birthdays based on the date of birth field. Users can view birthday notifications for contacts over three selectable timeframes: the next week, the next month, or the next three months. | ||
Additionally, the application provides a search functionality, allowing users to quickly locate specific contacts by searching for their names. This feature ensures efficient navigation through potentially large contact lists and enhances user experience by streamlining access to contact information.''' | ||
icon = 'person-vcard-fill' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
from datetime import datetime | ||
from re import sub | ||
|
||
from django.forms import ModelForm, CharField, EmailField, TextInput, \ | ||
DateField, DateInput | ||
from django.core.exceptions import ValidationError | ||
from phonenumbers import parse, is_valid_number, NumberParseException, \ | ||
format_number, PhoneNumberFormat | ||
|
||
from .models import Contact | ||
|
||
|
||
PHONE = '+380776665544' | ||
|
||
|
||
class ContactsForm(ModelForm): | ||
current_year = datetime.now().year | ||
|
||
name = CharField( | ||
min_length=1, | ||
max_length=50, | ||
required=True, | ||
widget=TextInput({'class': 'form-control'}), | ||
) | ||
|
||
address = CharField( | ||
min_length=10, | ||
max_length=150, | ||
required=False, | ||
widget=TextInput({'class': 'form-control'}), | ||
) | ||
|
||
phone = CharField( | ||
min_length=10, | ||
max_length=20, | ||
required=True, | ||
widget=TextInput({'class': 'form-control', 'placeholder': PHONE}), | ||
) | ||
|
||
email = EmailField( | ||
min_length=5, | ||
max_length=50, | ||
required=False, | ||
widget=TextInput({ | ||
'class': 'form-control', | ||
'type': 'email', | ||
'id': 'email', | ||
'name': 'email', | ||
'placeholder': 'example@example.com', | ||
}), | ||
) | ||
|
||
birthday = DateField( | ||
required=False, | ||
widget=DateInput({'class': 'form-control', 'type': 'date'}) | ||
) | ||
|
||
class Meta: | ||
model = Contact | ||
fields = ('name', 'address', 'phone', 'email', 'birthday') | ||
|
||
def __init__(self, *args, **kwargs): | ||
self.user = kwargs.pop('user', None) | ||
|
||
super().__init__(*args, **kwargs) | ||
|
||
for field in self.fields: | ||
if self.errors.get(field): | ||
self.fields[field].widget.attrs['class'] += ' is-invalid' | ||
else: | ||
self.fields[field].widget.attrs['class'] += ' form-control' | ||
|
||
def clean_phone(self): | ||
if phone := self.cleaned_data.get('phone'): | ||
phone = sub(r'(?<!^)\D+', '', phone) | ||
|
||
incorrect = ValidationError( | ||
'Please enter your phone number in the international format ' | ||
f'(e.g., {PHONE}).', | ||
) | ||
|
||
try: | ||
if ( | ||
Contact.objects | ||
.filter(user=self.user, phone=phone) | ||
.exclude(id=self.instance.id if self.instance else None) | ||
.exists() | ||
): | ||
raise ValidationError( | ||
'A contact with this phone number already exists.', | ||
) | ||
|
||
parsed = parse(phone, None if phone.startswith('+') else 'UA') | ||
|
||
if not is_valid_number(parsed): | ||
raise incorrect | ||
|
||
return format_number(parsed, PhoneNumberFormat.E164) | ||
|
||
except NumberParseException: | ||
raise incorrect | ||
|
||
return phone | ||
|
||
def clean_email(self): | ||
if ( | ||
(email := self.cleaned_data.get('email')) and | ||
|
||
Contact.objects | ||
.filter(user=self.user, email=email) | ||
.exclude(id=self.instance.id if self.instance else None) | ||
.exists() | ||
): | ||
raise ValidationError('A contact with this e-mail already exists.') | ||
|
||
return email |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# Generated by Django 5.1.1 on 2024-10-10 13:06 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [ | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='Contact', | ||
fields=[ | ||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('name', models.CharField(max_length=50)), | ||
('address', models.CharField(max_length=150, null=True)), | ||
('phone', models.CharField(max_length=12, unique=True)), | ||
('email', models.EmailField(blank=True, max_length=50, null=True, unique=True)), | ||
('birthday', models.DateField(null=True)), | ||
], | ||
), | ||
] |
18 changes: 18 additions & 0 deletions
18
personal_assistant/pa_contacts/migrations/0002_alter_contact_phone.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Generated by Django 5.1.2 on 2024-10-11 16:03 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('pa_contacts', '0001_initial'), | ||
] | ||
|
||
operations = [ | ||
migrations.AlterField( | ||
model_name='contact', | ||
name='phone', | ||
field=models.CharField(max_length=20, unique=True), | ||
), | ||
] |
40 changes: 40 additions & 0 deletions
40
personal_assistant/pa_contacts/migrations/0003_contact_user.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# Generated by Django 5.1.2 on 2024-10-12 12:51 | ||
|
||
import django.db.models.deletion | ||
from django.conf import settings | ||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('pa_contacts', '0002_alter_contact_phone'), | ||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name='contact', | ||
name='user', | ||
field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, related_name='contacts', to=settings.AUTH_USER_MODEL), | ||
preserve_default=False, | ||
), | ||
migrations.AlterField( | ||
model_name='contact', | ||
name='email', | ||
field=models.EmailField(blank=True, max_length=50, null=True), | ||
), | ||
migrations.AlterField( | ||
model_name='contact', | ||
name='phone', | ||
field=models.CharField(max_length=20), | ||
), | ||
migrations.AddConstraint( | ||
model_name='contact', | ||
constraint=models.UniqueConstraint(fields=('user', 'phone'), name='unique_phone_per_user'), | ||
), | ||
migrations.AddConstraint( | ||
model_name='contact', | ||
constraint=models.UniqueConstraint(fields=('user', 'email'), name='unique_email_per_user'), | ||
), | ||
] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
from django.contrib.auth.models import User | ||
from django.db.models import ( | ||
CASCADE, | ||
CharField, | ||
DateField, | ||
EmailField, | ||
ForeignKey, | ||
Model, | ||
UniqueConstraint, | ||
) | ||
|
||
|
||
class Contact(Model): | ||
name = CharField(max_length=50, null=False) | ||
address = CharField(max_length=150, null=True) | ||
phone = CharField(max_length=20, null=False) | ||
email = EmailField(max_length=50, null=True, blank=True) | ||
birthday = DateField(null=True) | ||
|
||
user = ForeignKey(User, CASCADE, related_name='contacts') | ||
|
||
class Meta: | ||
constraints = [ | ||
UniqueConstraint( | ||
fields=['user', 'phone'], | ||
name='unique_phone_per_user', | ||
), | ||
UniqueConstraint( | ||
fields=['user', 'email'], | ||
name='unique_email_per_user', | ||
), | ||
] | ||
|
||
def save(self, *args, **kwargs): | ||
if self.email == '': | ||
self.email = None | ||
|
||
super().save(*args, **kwargs) | ||
|
||
def __str__(self): | ||
return self.name |
Oops, something went wrong.