*سلیب* که تمام بازیها را با باخت پشت سر گذاشته بود، بیخیال بازی کردن شد. او از خانه بیرون رفت تا به دنبال سرگرمی جدیدی باشد که ناگاه ردپای *سجاد* را بر روی زمین دید. او به دنبال آن ردپا رفت و در نهایت توانست یک شرکت جدید تاسیس کند.
شرکت *سلیب* از همان ابتدا برای خود هدفی پیدا کرد. این شرکت میخواهد برای خود سایتی راهاندازی کند تا بتواند مشتریهای بیشتری را جذب کند. به همین دلیل از *علی* و *کیانوش* در این راستا تقاضای کمک کرده است.
آنها هم چون در برنامهنویسی تازهکارند از شما درخواست کردهاند که این پروژه را برایشان پیادهسازی کنید.
برای این پروژه ما نیاز به پیادهسازی کلاسهای ```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 terminal terminal
>> 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>
## نکات
+ میتوانید فایل اولیه این سوال را از [این لینک](/problemset/assignments/4367/download_problem_initial_project/87184/) دریافت کنید.
+ شما میتوانید با پیادهسازی این سوال به صورت بخشبخش نمره بگیرید.
+ برای ارسال باید یک فایل پایتون ارسال کنید که در آن تابع `Account` و `Site` و دکوراتورها به شکل گفته شده وجود داشته باشد.
## نحوه ارسال
شما باید کلاسهای `Account` و `Site` موجود در فایل `source.py` را تکمیل و سپس این فایل را ارسال کنید.