*فرهاد* که به تازگی در کوئرا استخدام شده، تصمیم گرفته برای کمک به بخش [Careers کوئرا](https://talent.quera.ir/) تعدادی دستور طراحی کند. او که تسلط کمی به خط فرمان دارد و فقط توانسته مدل شرکت را تکمیل کند و به علت همزمان شدن امتحاناتش با زمان تحویل، به کمک شما برای تکمیل دستورات نیاز دارد.
## پروژه اولیه
پروژه اولیه را از [این لینک](/problemset/assignments/4367/download_problem_initial_project/102249/) دریافت کنید.
ساختار فایلهای این پروژه به صورت زیر است.
```
CLI
├── career
│ ├── admin.py
│ ├── apps.py
│ ├── __init__.py
│ ├── management
│ │ ├── commands
│ │ │ ├── <mark class="yellow" title="این فایل را تغییر دهید"> > addCompany.py < </mark>
│ │ │ ├── <mark class="yellow" title="این فایل را تغییر دهید"> > collectCompany.py < </mark>
│ │ │ ├── <mark class="yellow" title="این فایل را تغییر دهید"> > editCompany.py < </mark>
│ │ │ ├── __init__.py
│ │ │ └── <mark class="yellow" title="این فایل را تغییر دهید"> > rmCompany.py < </mark>
│ │ └── __init__.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ ├── utils.py
│ └── views.py
├── config
│ ├── asgi.py
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py
├── requirements.txt
└── tests
├── __init__.py
└── testssample.py
```
برای کمک به *فرهاد*، دستورات خواسته شده را در فایلهای خواسته شده پیادهسازی کنید.
## جزئیات
<details class="blue">
<summary> **دستور** `addCompany` </summary>
میخواهیم درون فایل `addCompany.py`، دستوری برای جنگو بنویسیم که با اجرای آن، به ترتیب مقادیر *name*, *email*, *phone*, *description* (از چپ به راست) را از کاربر دریافت کند و با این مقادیر، یک نمونه از کلاس *Company* بسازد.
+ برای فیلدهايی که دارای ویژگی `null=False` هستند، کاربر حتما باید مقداری را وارد کند. در صورتی که کاربر بدون وارد کردن مقداری *Enter* زد باید خطای `Error: This field cannot be blank.` نمایش داده شود و برای فیلدهایی که دارای ویژگی `null=True` هستند، با زدن *Enter* به مرحله بعد رفته و مقدار آن فیلد *None* شود.
### اعتبارسنجی مخصوص هر فیلد:
<details class="grey">
<summary> **name** </summary>
فیلد `name` باید حداکثر ۵۰ کاراکتر باشد؛ در غیر اینصورت، عبارت
`Error: Ensure this value has at most 50 characters (it has COUNT).`
نمایش داده شود که به جای کلمهی *COUNT*، تعداد کاراکترهای عبارت شما قرار میگیرد.
همچنین این فیلد باید یکتا باشد؛ در غیر اینصورت، عبارت
`Error: That name is already taken.`
نمایش داده شود.
</details>
<details class="grey">
<summary> **email** </summary>
آدرس وارد شده باید یک ایمیل معتبر باشد، در غیر اینصورت باید عبارت
`Error: Enter a valid email address.`
نمایش داده شود.
</details>
<details class="grey">
<summary> **phone** </summary>
یک شماره موبایل در صورتی معتبر است که یکی از حالتهای زیر را دارا باشد:
* شامل ۱۱ رقم باشد و با 09 آغاز شود.
* شامل ۱۳ رقم باشد و با 98+ آغاز شود.
* شامل ۱۴ رقم باشد و با 0098 آغاز شود.
در غیر این صورت باید عبارت
`Error: Phone number format is not valid.`
نمایش داده شود.
</details>
<details class="grey">
<summary> **description** </summary>
این فیلد محدودیتی ندارد.
</details>
### نکات مهم
* در صورت وارد شدن مقدار غیر قابل قبول برای هر فیلد، خط فرمان همچنان منتظر وارد شدن مقدار قابل قبول برای همان فیلد میماند.
* برای چاپ خطاها، حتماً از دستور `self.stderr.write(error_msg)` استفاده کنید.
### نمونه
![توضیح تصویر](https://quera.org/qbox/view/YRu9dIHEfh/addcompany.png)
</details>
</details>
<details class="blue">
<summary> **دستور** `editCompany` </summary>
میخواهیم درون فایل `editCompany.py`، دستوری برای جنگو بنویسیم که یک *positional arguments* به عنوان نام شرکت و همچنین چهار *optional arguments* (`--description` ,`--phone` ,`--email` ,`--name`) دریافت و فیلدهای مربوط به آن شرکت را بروز کند.
+ برای فیلدهايی که دارای ویژگی `null=False` هستند، اگر آرگومان آنها برابر `''` بود؛ استثنای *CommandError* با پیام `Field cannot be blank.` رخ دهد (به جای کلمهی `Field`، نام فیلد با حرف ابتدای **بزرگ** قرار میگیرد.) اما برای فیلدهایی که دارای ویژگی `null=True` هستند، میتواند برابر `''` باشد؛ ولی مقدار فیلد تغییری نمیکند.
+ در هر مرحله از اعتبار سنجی که در ادامه اشاره میشود، اگر مقادیر، معتبر نبودند باید استثنای *CommandError* با پیام خطای مربوط رخ دهد.
+ موارد، به ترتیب نوشتهشده باید بررسی شوند.
<details class="grey">
<summary> **positional arguments** </summary>
در صورتی که شرکتی با این نام وجود نداشت؛ باید یک `Exception` با پیام
`Company matching query does not exist.`
رخ دهد.
</details>
<details class="grey">
<summary> **optional arguments** </summary>
<details class="yellow">
<summary> **name** </summary>
آرگومان `name` باید حداکثر ۵۰ کاراکتر باشد؛ در غیر اینصورت، یک `Exception` با پیام
`Error: Ensure this value has at most 50 characters (it has NUM).`
رخ دهد که به جای *NUM* تعداد کاراکترهای عبارت شما قرار میگیرد.
همچنین این فیلد باید یکتا باشد، در غیر اینصورت، باید یک `Exception` با پیام
`Error: That name is already taken.`
رخ دهد.
</details>
<details class="yellow">
<summary> **email** </summary>
آدرس وارد شده باید یک ایمیل معتبر باشد در غیر این صورت باید یک `Exception` با پیام
`Error: Enter a valid email address.`
رخ دهد.
</details>
<details class="yellow">
<summary> **phone** </summary>
یک شماره موبایل در صورتی معتبر است که یکی از حالتهای زیر را دارا باشد:
* شامل ۱۱ رقم باشد و با 09 آغاز شود.
* شامل ۱۳ رقم باشد و با 98+ آغاز شود.
* شامل ۱۴ رقم باشد و با 0098 آغاز شود.
در غیر این صورت باید یک `Exception` با پیام
`Error: Phone number format is not valid.`
رخ دهد.
</details>
<details class="yellow">
<summary> **description** </summary>
این آرگومان محدودیتی ندارد.
</details>
</details>
### نمونه
![توضیح تصویر](https://quera.org/qbox/view/x8lb4UYrgI/editcompany.png)
</details>
</details>
<details class="blue">
<summary> **دستور** `rmCompany` </summary>
میخواهیم درون فایل `rmCompany.py`، برای جنگو بنویسیم که نام تعدادی (صفر یا بیشتر) شرکت را دریافت کند و درصورتی که شرکتی با آن نامها وجود داشت آن را از پایگاه داده حذف کند.
<details class="grey">
<summary> **positional argument** </summary>
در صورتی که شرکتی با این نامها وجود نداشت، به ازای هر نام غیرمعتبر باید عبارت
`COMPANY matching query does not exist.`
نمایش داده شود، که به جای *COMPANY* نام شرکت وارد شده قرار میگیرد.
</details>
<details class="grey">
<summary> **optional argument** </summary>
در صورت وجود `--all` در دستور، بدون توجه به نام شرکتهای وارد شده (در صورت وجود)، تمام شرکتها حذف میشوند.
</details>
### نکات مهم
+ برای چاپ خطا، حتماً از دستور `self.stderr.write(error_msg)` استفاده کنید.
### نمونه
![توضیح تصویر](https://quera.org/qbox/view/NfecYkcqrY/rmcompany.png)
</details>
<details class="blue">
<summary> **دستور** `collectCompany` </summary>
میخواهیم درون فایل `collectCompany.py`، دستوری برای جنگو بنویسیم که با اجرای آن، یک فایل *CSV* به نام `company.csv` شامل اطلاعات تماس شرکتها، در ریشهی پروژه ایجاد شود.
+ ستونهای فایل *CSV* به ترتیب (از چپ به راست) نام، ایمیل و تلفن شرکت است.
</details>
## تست نمونه
در فایلهای اولیهای که دانلود کردید یکسری داده اولیه به عنوان نمونه قرار داده شده است. میتوانید قبل از فرستادن سوال در سایت، این تستها را ببینید تا با نحوه داوری ما آشنا شوید و از پاسخ خود اطمینان حاصل نمایید.
تستها را میتوانید با دستور زیر اجرا کنید:
```shell terminal terminal
python manage.py test
```
## نکات
+ توجه کنید که در قسمتهایی که گفته شده عبارت مورد نظر نمایش داده شود، باید از دستور `self.stderr.write(error_msg)` و در قسمتهایی که گفته شده یک `Exception` رخ دهد، از دستور `raise` استفاده کنید.
+ شما تنها مجوز ایجاد تغییرات در فایلهای زیر را دارید و **تمامی تغییرات دیگر شما** در فایلهای پروژه **نادیده گرفته خواهند شد.**
+ فایل `career/management/commands/addCompany.py`
+ فایل `career/management/commands/collectCompany.py`
+ فایل `career/management/commands/editCompany.py`
+ فایل `career/management/commands/rmCompany.py`
## نحوه ارسال
یک فایل _ZIP_ حاوی همهی فایلهای پروژه، آپلود کنید. نام فایل _ZIP_ اهمیتی ندارد.