کوئرا قصد دارد تا آپلودسنتری برای کاربران سامانه *LMS* اش طراحی کند. بدین ترتیب کاربران میتوانند فایلهای آموزشی موردنیاز خود برای هر کلاس را داخل صفحه کلاس آپلود کنند و لینکش را با بقیه به اشتراک قرار دهند. از آنجایی که همه آپلودسنترها دارای محدودیتهایی برای آپلود فایل هستند و تیم فنی کوئرا وقتی برای پیادهسازی این سامانه ندارند، از شما میخواهیم تا *API* آپلودسنتر مدنظرشان را طراحی کنید.
## پروژه اولیه
پروژه اولیه را از [این لینک](/contest/assignments/51094/download_problem_initial_project/174130/) دانلود کنید. ساختار فایلهای این پروژه به صورت زیر است:
```
django_upload_center
├── account
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── __init__.py
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
├── config
│ ├── __init__.py
│ ├── asgi.py
│ ├── <mark title="این فایل را میتوانید تغییر دهید" class="teal">settings.py</mark>
│ ├── urls.py
│ └── wsgi.py
├── tests
│ └── testssample.py
├── upload_center
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── <mark title="این فایل را تغییر دهید" class="yellow">serializers.py</mark>
│ ├── tests.py
│ ├── urls.py
│ └── <mark title="این فایل را تغییر دهید" class="yellow">views.py</mark>
├── manage.py
└── requirements.txt
```
### اپلیکشن `account`
اپلیکیشن `account` شامل دو مدل میشود که محدودیتهای کاربران را مشخص میکنند. در ادامه فیلدهای هر یک توضیح داده شده است.
<details class="green">
<summary> **مدل `User`** </summary>
این مدل شامل فیلدهای زیر است:
+ فیلد `account` که مشخص کننده نوع حساب کاربری یک `User` است.
+ فیلد `used_storage` که مقدار فضای اشغال شده در سرور توسط `User` را بر حسب بایت نشان میدهد.
</details>
<details class="green">
<summary> **مدل `Account`** </summary>
این مدل شامل فیلدهای زیر است:
+ فیلد `title` که عنوان اکانت را مشخص میکند.
+ فیلد `storage` که حداکثر فضای مربوط به این نوع حساب کاربری را بر حسب بایت مشخص میکند.
+ فیلد `max_file_transfer` که حداکثر محدودیت حجمی فایلهای آپلود شده مربوط به این نوع حساب کاربری را بر حسب بایت مشخص میکند.
</details>
## پیادهسازی مورد انتظار
**اکیدا توصیه میشود** پیش از حل این بخش سایر قسمتهای پروژه را مطالعه کنید.
<details class="yellow">
<summary> **ویو `UploadFile`** </summary>
متد `PUT` در این ویو، وظیفه آپلود فایل کاربران را بر عهده دارد و در پاسخ یک دیکشنری به صورت فایل `JSON` مانند زیر برمیگرداند:
```json
{
"city-g948bcbda2_640.jpg": "/fm/download/kian/city-g948bcbda2_640.jpg",
"a-month-ga3e069e51_640.jpg": "/fm/download/kian/a-month-ga3e069e51_640.jpg",
"Kargah.rar": "You don't have enough space to upload this file!",
"Tedx_Intro.pdf": "You can't upload files more than 1.000 Megabytes!"
}
```
در این دیکشنری، کلیدها نام فایل ذخیره شده در سرور و مقدار آنها آدرس دانلود فایل مربوطه میباشد.
در صورتی که حجم فایل ارسالی از محدودیت مشخص شده (`max_file_transfer`) بیشتر باشد، کلید دیکشنری اسم فایل ارسالی و مقدار آن باید عبارت زیر باشد:
```
You can't upload files more than <mark class="red" title="محدودیت حجمی کاربر با ۳ رقم اعشار"><user_max_file_transfer></mark> Megabytes!
```
و در صورتی که کاربر فضایی برای آپلود فایل جدید نداشت، کلید دیکشنری اسم فایل ارسالی و مقدار آن باید عبارت زیر باشد:
```
You don't have enough space to upload this file!
```
### نکات مهم
+ فایلهای کاربران باید در `root` پروژه و در دایرکتوری زیر ذخیره شوند.
```
.uploaded_files/<mark class="red" title="یوزرنیم کاربر آپلودکننده"><username></mark>
```
+ کاربران باید امکان آپلود چندین فایل را به صورت همزمان داشته باشند.
+ در صورتی که یک فایل خالی آپلود شود، استاتوس کد ۴۰۰ را برگردانید.
+ آپلودسنتر **حداکثر** تعداد فایلی که از کاربر دریافت میکند را باید با توجه به محدودیتهای اکانت کاربر آپلود کند.
+ کاربران باید بتوانند فایلهایی تکراری یا با نام یکسان آپلود کنند.
+ برای مدیریت آپلود فایلهایی با نام مشابه، فقط به انتهای نام فایل میتوانید یک شناسه (_suffix_) اضافه کنید و نام فایل نباید تغییری کند.
+ برای تبدیل کیلوبایت به مگابایت تنها کافیست که عدد موردنظر را بر عدد ۱۰۲۴ تقسیم کنید.
</details>
<details class="yellow">
<summary> **ویو `FileManager`** </summary>
<details class="orange">
<summary> متد `GET` </summary>
در این متد، اطلاعات کاربر به صورت یک فایل `JSON` مانند زیر برمیگردد:
```json
{
<mark title="نوع اکانت کاربر" class="red">"Account"</mark>: "Pro",
<mark title="میزان فضای ذخیرهسازی کاربر برحسب مگابایت (با ۳ رقم اعشار)" class="red">"Storage"</mark>: 5.000,
<mark title="میزان فضای مصرف شده توسط کاربر برحسب مگابایت (با ۳ رقم اعشار)" class="red">"Used"</mark>: 0.916,
<mark title="نام فایلهای ذخیره شده در سرور توسط کاربر" class="red">"Files"</mark>: [
"a-month-ga3e069e51_640.jpg",
"city-g948bcbda2_640.jpg"
]
}
```
+ در صورتی که کاربر فایلی در سرور نداشت، متن زیر را به عنوان مقدار کلید `Files` قرار دهید:
```
<mark class="red" title="یوزرنیم کاربر"><username></mark> doesn't have any files!
```
</details>
<details class="orange">
<summary> متد `DELETE` </summary>
در این متد، نام فایلی که قرار است حذف شود در پارامتر `file_name` از طریق دیکشنری `POST` شئ ریکوئست در دسترس است. در صورتی که فایل موردنظر در سرور وجود داشت، فایل را از سرور حذف کنید، اطلاعات کاربر را بهروز کنید و پاسخ زیر را در قالب `JSON` به کاربر نشان دهید:
```json
{
"detail": "<mark class="red" title="نام فایل"><FILE_NAME></mark> Deleted Successfully."
}
```
و در صورتی که فایل در سرور موجود نبود، پاسخ زیر را در قالب `JSON` و با استاتوس کد ۴۰۴ به کاربر ارائه دهید:
```json
{
"detail": "<mark class="red" title="نام فایل"><FILE_NAME></mark> hasn't existed!"
}
```
</details>
</details>
<details class="yellow">
<summary> **ویو `DownloadFile`** </summary>
در متد `GET` این ویو، فایل خواسته شده را به صورت یک `FileResponse` برگردانید.
+ پارامتر `user` نام کاربری کاربر آپلودکننده فایل است و پارامتر `filename` نام فایل را مشخص میکند.
+ در صورتی که فایل درخواست شده موجود نباشد، یک دیکشنری به فرمت زیر و با کد وضعیت ۴۰۴ برگردانید:
```python
{"detail": f"{filename} hasn't existed!"}
```
</details>
## نکات
+ شما تنها مجوز ایجاد تغییرات در فایلهای `upload_center/views.py`، `config/settings.py` و `upload_center/serializers.py` را دارید و **تمامی تغییرات دیگر شما** در فایلهای پروژه **نادیده گرفته خواهند شد.**
+ فراموش نکنید که میتوانید با مطالعهی `testssample.py` با روش تست کردن مدلها، فرمها و ویوها آشنا شوید.
## نحوه ارسال
یک فایل _ZIP_ حاوی همهی فایلهای پروژه، آپلود کنید. نام فایل _ZIP_ اهمیتی ندارد.