*سلیب* که تمام بازیها را با باخت پشت سر گذاشته بود، بیخیال بازی کردن شد. او از خانه بیرون رفت تا به دنبال سرگرمی جدیدی باشد که ناگاه ردپای *سجاد* را بر روی زمین دید. او به دنبال آن ردپا رفت و در نهایت توانست یک شرکت جدید تاسیس کند.
شرکت *سلیب* از همان ابتدا برای خود هدفی پیدا کرد. این شرکت میخواهد برای خود سایتی راهاندازی کند تا بتواند مشتریهای بیشتری را جذب کند. به همین دلیل از *علی* و *کیانوش* در این راستا تقاضای کمک کرده است.
آنها هم چون در برنامهنویسی تازهکارند از شما درخواست کردهاند که این پروژه را برایشان پیادهسازی کنید.
# توضیحات پروژه
برای این پروژه ما نیاز به یک کلاس ```Account``` و یک کلاس ```Site``` داریم.
<details class="blue">
<summary> **کلاس** ```Account``` </summary>
هر شی این کلاس دارای پنج ویژگی ($Attribute$) است که در تابع `ــinitــ` مقداردهی میشود و به ازای هر شیء مقدار آن فرق دارد:
<details class="green">
<summary> **۱. یوزرنیم** ```username``` </summary>
یوزرنیم کاربر باید به فرمت (نام + آندر لاین + نام خانوادگی) ذخیره شود. توجه کنید که نام و نام خانوادگی فقط باید از حروف انگلیسی باشند.
در صورتی که یوزرنیم دریافتی مانند فرمت بالا نبود باید یک $Exception$ با متن اررور ```invalid username``` *raise* کنید.
مثال:
* اگر یوزرنیم برابر با ```ali_babaei``` بود مشکلی ندارد.
* اما اگر یوزرنیم برابر با ```kianoush_nasr_azadani``` بود باید $Exception$ خواسته شده را *raise* کنید.
</details>
<details class="green">
<summary> **۲. پسورد** ```password``` </summary>
پسورد کاربر باید دارای نکات زیر باشد:
* طول آن حداقل ۸ کاراکتر باشد.
* حتما شامل یک حرف بزرگ، یک حرف کوچک، یک عدد باشد.
* باید حتما به صورت هش(`hash`) با فرمت ```sha256``` و `encode` ```utf-8``` ذخیره شود.
در صورتی که پسورد دریافتی مانند فرمت بالا نبود باید یک $Exception$ با متن اررور ```invalid password``` *raise* کنید.
مثال:
* اگر پسورد برابر با ```Aa123123``` بود مشکلی ندارد و صحیح است.
* ولیکن اگر پسورد برابر با ```Aa12312``` ```AA123123``` ```aa123123``` valid نیست.
</details>
<details class="green">
<summary> **۳. کد ملّی** ```user_id``` </summary>
کدملّی کاربر باید اعتبار سنجی شود
الگوریتم اعتبارسنجی کد ملی:
کد ملی شمارهای است ۱۰ رقمی که از سمت چپ سه رقم کد شهرستان محل صدور شناسنامه، شش رقم بعدی کد منحصربهفرد برای فرد دارندهی شناسنامه در شهرستان محل صدور و رقم آخر آن هم یک رقم کنترل است که از روی ۹ رقم سمت چپ بهدست میآید. برای بررسی کنترل کد کافیست مجدد از روی ۹ رقم سمت چپ رقم کنترل را محاسبه کنیم
![نمایش جایگاههای کدملی](https://quera.ir/qbox/download/Dpv5j0bHdH/code_melli1.png)
۱- برای محاسبهی رقم کنترل از روی سایر ارقام، هر رقم را در موقعیت آن ضرب کرده و حاصل را با هم جمع میکنیم.
۲- مجموع بهدست آمده از مرحله یک را بر ۱۱ تقسیم میکنیم.
۳- اگر باقیمانده کمتر از ۲ باشد، رقم کنترل باید برابر باقیمانده باشد در غیر اینصورت رقم کنترل باید برابر یازده منهای باقیمانده باشد.
مثال : آیا کد ۷۷۳۱۶۸۹۹۵۱ یک کد ملی معتبر است؟
![توضیح مثال کدملی](https://quera.ir/qbox/download/xKyq6tjjwM/code_melli2.png)
حاصل جمع ضرب ارقام ۲ الی ۱۰ را در موقعیت آنها محاسبه میکنیم:
`۱۰+۲۷+۳۶+۴۰+۳۶+۷+۲۴+۶۳+۷۰ = ۳۱۳`
باقیمانده تقسیم ۳۱۳ بر ۱۱ = ۵
چون باقیمانده برابر با ۵ و بزرگتر یا مساوی ۲ است پس باید رقم کنترل این کد برابر ۶(یازده منهای پنج برابر است با شش)باشد.
در صورتی که کدملّی دریافتی مانند فرمت بالا نبود باید یک $Exception$ با متن اررور ```invalid code melli``` *raise* کنید.
مثال:
* اگر کدملّی برابر با ```0024848484``` بود مشکلی نداشته و valid است.
* ولیکن اگر کد ملی برابر با ```0024848483``` valid نیست.
</details>
<details class="green">
<summary> **۴. شماره موبایل** ```phone``` </summary>
شماره تلفن کاربر ممکن است به دو فرمت به شما داده شود:
۱- ```+989xxxxxxxxx```
۲- ```09xxxxxxxxx```
اگر شماره صحیح بود در هر حالتی شما باید آن را به صورت ```09xxxxxxxxx``` ذخیره کنید،
و در صورتی که شماره موبایل دریافتی مانند فرمت بالا نبود باید یک $Exception$ با متن اررور ```invalid phone number``` *raise* کنید.
مثال:
* اگر شمارهی موبایل برابر با ```09121212121``` باشد مشکلی ندارد.
* ولیکن اگر شمارهی موبایل برابر با ```08121212121```, ```0912121212```, ```+988121212121```, ```+98912121212``` باشد valid نیست.
</details>
<details class="green">
<summary> **۵. ایمیل** ```email``` </summary>
تضمین میشود ایمیل ورودی به فرمت ```First_Part@Second_Part.Third_Part``` به شما داده میشود.
ایمیل کاربر باید صحیح باشد، ایمیلی را صحیح مینامیم اگر و فقط اگر:
* ```First_Part``` و ```Second_Part``` تنها شامل حروف بزرگ و کوچک، اعداد، ` _ `، ` - `، ` . ` باشد و تضمین میشود نیازی به چک کردن محدودیت طولی بخش اول و دوم ندارید.
* ```Third_Part``` باید تنها شامل حروف بزرگ و کوچک باشد و طولی میان ۲ تا ۵ کاراکتر داشته باشد.
در صورتی که ایمیل دریافتی مانند فرمت بالا نبود باید یک $Exception$ با متن اررور ```invalid email``` *raise* کنید.
</details>
### دکوراتورها
<details class="green">
<summary> **تابع** ```show_welcome``` </summary>
این تابع بر اساس آنچه به عنوان کد اولیه پیادهسازی شده است شما باید آن را به نحوی پیادهسازی کنید که در ابتدا نام کاربری شخص (```username```) را که شامل آندرلاین است را برداشته و به جای آن `space` قرار داده و حروف اول آن را به صورت capital (بزرگ) بنویسد.؛
اگر طول این کلمه بیشتر از ۱۵ کاراکتر باشد (به همراه کاراکتر فاصله) هم ادامه آن را حذف کرده و به آخر آن ```...``` قرار دهد و در آخر تابع ```welcome``` فراخوانی کند.
مثال:
```python
>> print(user.username)
> kianoush_nasr
>> print(user2.username)
> salib_alibabaeei
>> welcome(user)
> welcome to our site Kianoush Nasr
>> welcome(user2)
> welcome to our site Salib Alibabaee...
```
</details>
<details class="green">
<summary> **تابع** ```verify_change_password``` </summary>
این تابع به عنوان کد اولیه پیادهسازی شده است و شما تنها نیاز است که اگر پسورد قبلی شما با ```old_password``` یکسان بود پسورد جدید را برای ```user``` قرار دهد و در آخر تابع ```change_password``` را فراخوانی کند.
</details>
<details class="green">
<summary>**توجه**</summary>
* پسورد در هنگام تغییر یا در حالت set کردن باید به صورت هش شده ذخیره گردد.
* کلاس یوزر باید دارای تابعی به نام ```set_new_password``` باشد که پسورد یوزر را عوض میکند(در این مرحله نیز باید چک کنید پسورد جدید دارای نکات گفته شده باشد و اگر بود باید آن را به صورت هش ذخیره کنید)
</details>
</details>
<details class="blue">
<summary> **کلاس** ```Site``` </summary>
هر شی این کلاس دارای سه ویژگی ($Attribute$) است که در تابع `ــinitــ` مقداردهی میشود و به ازای هر شیء مقدار آن فرق دارد:
### ۱. آدرس سایت ```url```
تضمین میشود آدرس سایت صحیحی به شما داده میشود و نیازی به چک کردن آن ندارید.
### ۲. افراد ثبت نام شده ```register_users```
در هنگام ساخت شیء این مقدار را برابر با یک لیست خالی قرار دهید.
### ۳. افراد فعال در سایت ```active_users```
در هنگام ساخت شیء این مقدار را برابر با یک لیست خالی قرار دهید.
### جزئیات
<details class="green">
<summary> **تابع** ```register``` </summary>
کلاس سایت باید دارای تابعی به نام ```register``` باشد. این تابع یک شیء از کلاس یوزر ورودی میگیرد و اگر آن یوزر در ```register_users``` خود سایت بود یک $Exception$ با متن ارور ```user already registered``` *raise* کنید وگرنه آن را به لیست ```register_users``` خود سایت بیفزایید و عبارت ```register successful``` را برگردانید.
توجه کنید که لیست `register_users` باید به ترتیب ثبتنام کاربران، آنها را در خود ذخیره کند.
</details>
<details class="green">
<summary> **تابع** ```login``` </summary>
کلاس سایت باید دارای تابعی به نام ```login``` باشد. این تابع ورودیهای متفاوتی میگیرد:
۱- ممکن است ```email``` و ```username``` و ```password``` به آن داده شود. در این حالت شما باید چک کنید یوزری در ```register_users``` سایت وجود دارد که هم یوزرنیم و هم ایمیلی برابر با ورودی داشته باشد و پسوردش برابر با هش شدهی پسورد ورودی باشد یا خیر.
۲- ممکن است ```username``` و ```password``` به آن داده شود. در این حالت شما باید چک کنید یوزری در ```register_users``` سایت وجود دارد که یوزرنیمی برابر با ورودی داشته باشد و پسوردش برابر با هش شدهی پسورد ورودی باشد یا خیر.
۳- ممکن است ```email``` و ```password``` به آن داده شود. در این حالت شما باید چک کنید یوزری در ```register_users``` سایت وجود دارد که ایمیلی برابر با ورودی داشته باشد و پسوردش برابر با هش شدهی پسورد ورودی باشد یا خیر.
* باید با توجه به اینکه ورودی تابع از کدام نوع است لاگین(*login*) را با اطلاعات ورودی تطبیق دهید.
* اگر کاربری در هرکدام از حالات بالا یافت شد باید آن شیء یوزر را به لیست ```active_users``` اضافه کنید و عبارت ```login successful``` را برگردانید.
* اگر در هر کدام از حالات قبل از تطبیق اطلاعات یوزر در لیست ```active_users``` موجود بود باید عبارت ```user already logged in``` را برگردانید.
* اگر اطلاعات ورودی برای هیچکدام از کاربرهای ```register_users``` نبودند باید عبارت ```invalid login``` را برگردانید.
توجه کنید که لیست `active_users` باید به ترتیب ورود کاربران، آنها را در خود ذخیره کند.
</details>
<details class="green">
<summary> **تابع** ```logout``` </summary>
کلاس سایت باید دارای تابعی به نام ```logout``` باشد. این تابع یک شیء از کلاس یوزر ورودی میگیرد و اگر آن یوزر در ```active_users``` خود سایت بود آن را از لیست حذف کرده و عبارت ```logout successful``` را برگردانید و در صورت عدم وجود آن یوزر در ```active_users``` سایت عبارت ```user is not logged in``` را برگردانید.
</details>
</details>
<details class="red">
<summary> **نکات** </summary>
+ شما میتوانید با پیادهسازی این سوال به صورت بخشبخش نمره بگیرید.
+ برای ارسال باید یک فایل پایتون ارسال کنید که در آن تابع `Account` و `Site` و دکوراتورها به شکل گفته شده وجود داشته باشد.
+ میتوانید فایل اولیهی خام را با استفاده از این [این لینک](https://quera.ir/qbox/download/khtE508pcu/pool_initial.zip) دانلود کنید.
</details>