حتماً دیدهاید که در بخش عضویت بسیاری از وبسایتها برای امنیت بیشتر محدودیتهایی
بر روی نام کاربری و گذرواژه وجود دارد.
میخواهیم با پایتون ۳ تابعی بنویسیم که تعدادی نام کاربری و گذرواژه دریافت کند و بر اساس قواعدی
معتبر بودن آنها را بررسی کند و در نهایت لیست نامهای کاربری مجاز به عضویت را برگرداند.
# جزئیات
قواعد زیر را برای نام کاربری و گذرواژه در نظر گرفتهایم:
- نامهای کاربری `quera` و `codecup` را میخواهیم برای خودمان نگه داریم.
کسی مجاز به عضویت با این نامهای کاربری نیست.
- نام کاربری کمتر از ۴ حرف بسیار کوتاه است و مجاز نیست.
- همچنین برای امنیت کاربران، کاربری که رمز عبور او کمتر از ۶ حرف باشد یا
فقط از اعداد تشکیل شدهباشد نیز مجاز به عضویت نیست.
تابعی با نام `check_registration_rules` بنویسید که نام کاربری و گذرواژهی تعدادی کاربر را مانند نمونهی
زیر دریافت کند و در خروجی لیستی از نامهای کاربری مجاز به عضویت را برگرداند.
ترتیب اعضای لیست خروجی اهمیت ندارد.
```python
>>> check_registration_rules(username='password', sadegh='He3@lsa', quera='kLS45@l$')
['username', 'sadegh']
>>> check_registration_rules(saeed='1234567', ab='afj$L12')
[]
```
# نکات
- یک فایل Zip شامل یک فایل به نام `source.py` که تابع `check_registration_rules` در آن قرار دارد آپلود کنید.
- نام فایل Zip اهمیت ندارد.
ثبت نام
میخواهیم یک فروشگاه آنلاین را پیادهسازی کنیم که در آن کالاهایی برای بفروش بگذاریم و کاربران آنها را خریداری کنند و نظرات خود را راجع به کالاها بگذارند. بخشی از کد فروشگاه توسط فردی زده شدهاست و در حال حاضر در دسترس نیست. شما باید به آن امکاناتی اضافه کنید.
کد پروژه را میتوانید از این [لینک](http://s9.picofile.com/file/8339074692/digikala_initial.zip.html) دریافت کنید.
داخل پروژه ۲ فایل پایتون با نامهای `models.py` و `store.py` است. شما باید توابع ناقص فایل `store.py` را تکمیل کنید.
### جزئیات پروژه
داخل کلاس `Store` چند تابع وجود دارد که نیاز به تکمیل دارند. همچنین `object` آن دارای کالاهای موجود و کاربران ثبت نام شدهاست. کالاها به صورت `dictonary` ذخیره شدهاند که هر `key` آن یک کالا(`Product`) است و هر `value` آن تعداد موجودی آن کالاست. کاربران هم به صورت `list` ذخیره شدهاند.
وظیفهی شما این است که توابع زیر را برای کلاس `Store` پیادهسازی کنید.
+ تابع `remove_product`
این تابع وظیفهی حذف کالا از فروشگاه را دارد.در صورت موجود نبود این تعداد کالا، استثایی با پیام `Not Enough Products` پرتاب شود و هیچ کالایی از فروشگاه حذف نشود. در صورتی که موجودی یک کالا به صفر برسد باید از دیکشنری کالاها حذف شود.
+ تابع `add_user`
این تابع یک کاربر را به کاربران اضافه میکند. در صورتی که نام کاربری تکراری باشد کاربری اضافه نمیکند و مقدار `None` بر میگرداند؛ در غیر این صورت، رشتهی نام کاربری آن کاربر را برمیگرداند.
+ تابع `get_total_asset`
این تابع جمع کل داراییهای فروشگاه را برمیگرداند (دارایی فروشگاه برابر مجموع قیمت کالاهای موجود است.)
+ تابع `get_total_profit`
این تابع مقدار سودی که فروشگاه کردهاست را برمیگرداند. مقدار سود فروشگاه برابر جمع قیمت کالاهای فروختهشده است. ( تعداد هریک از کالاهای لیست `bought_products` در کلاس `User` یک است.)
+ تابع `get_comments_by_user`
این تابع لیست **متن** نظراتی که یک کاربر خاص روی محصولات موجود در فروشگاه گذاشتهاست را بر میگرداند (ترتیب آنها مهم نیست)
+ تابع `get_inflation_affected_product_names`
این تابع لیستی از **نام** کالاهایی که تورم روی آنها تاثیر گذاشتهاست را برمیگرداند. (تورم روی کالاهایی اثر گذاشتهاست که کالایی هم اسم آنها ولی با قیمت کمتر داخل فروشگاه وجود داشتهباشد). توجه کنید که اگر چند قیمت از یک کالا موجود باشد، نام آن فقط یک بار باید در لیست بیاید. ترتیب اعضای لیست اهمیتی ندارد.
+ تابع `clean_old_comments`
این تابع ورودی یک تاریخ از نوع `datetime` میگیرد و همهی نظراتی که قبل از آن تاریخ روی پستها گذاشته شدهاند را حذف میکند.
+ تابع `get_comments_by_bought_users`
این تابع لیستی از **متن** نظراتی را بر میگرداند که صاحب آن، کالا را خریداری کردهاست.
### نکات
+ توجه کنید که ممکن است از یک کالا چند عدد موجود باشد.
+ شما تنها مجاز به تغییر فایل `store.py` هستید.
#### آنچه باید آپلود کنید:
+ یک فایل *Zip* شامل یک فایل به نام `store.py` که کلاس `Store` در آن قرار دارد آپلود کنید.
+ نام فایل *Zip* اهمیت ندارد.
دیجی کالا
می خواهیم برنامه ای بنویسیم که با کمک آن بتوانیم فایل های خود را مدیریت کنیم.
##### نیاز هایی که باید این فایل منیجر برطرف کند عبارتند از:
* ساخت یک پوشه جدید
* ساخت یک فایل جدید
* جستجو کردن بر اساس نام فایل
* حذف فایل
* بازیابی فایل
## جزئیات
با توجه به نیازمندیهایی که در بالا گفته شد، یک کلاس با نام `FileManager` تعریف کنید که همه متدهای جدول زیر را طبق رفتار توضیح داده شده پیادهسازی کند.
| رفتار |خروجی| متد |
|:------------------:|:-------:|:-----------:|
|یک پوشه جدید با نام `name` در آدرس `address` می سازد.|`-`| `create_dir(name, address)` |
|یک فایل جدید با نام `name` در آدرس `address` ایجاد می کند .|`-`| `create_file(name, address)`|
|یک فایل با نام `name` را از آدرس `address` حذف می کند .|`-`| `delete(name, address)`|
|آدرس تمام فایل های با نام `name` را از آدرس `address` **به بعد** برمیگرداند|`List`| `find(name, address)`|
|فایل با نام `name` را بازیابی می کند.|`-`| `restore(name)`|
## نکات
* در متد `create_dir` در صورت عدم وجود پوشه باید پوشه ای جدید ساخته شود در غیر این صورت نباید هیچ اتفاقی بیفتد!
* در متد `create_file` در صورت عدم وجود فایل باید فایلی جدید ساخته شود در غیر این صورت نباید هیچ اتفاق دیگری بیفتد!
* در متد `delete` در صورت عدم وجود فایل نباید هیچ اتفاقی بیفتد
* در متد `find` باید یک لیست از تمام آدرس ها برگردانده شود ودر صورت عدم وجود فایلی با این نام یک لیست خالی برگردانده شود ترتیب این ادرس ها اهمیتی ندارد
* آدرسهای برگردانده شده از متد `find` باید از آدرس موجود در آرگومانها به بعد باشد (آدرس شامل خود آرگومان نیز میباشد)
.دقت شود که ممکن است فایل در سطوح پایین تر از سطح اول نیز موجود باشد.
* متد `restore`
* فایل بازیابی شده باید در آدرس قبلی خود قرار گیرد.
* اگر چند فایل هم نام بصورت متوالی حذف شوند باید با هربار صدا زدن متد `restore` فایلها به صورت متوالی و با عکس ترتیب حذف، بازیابی شوند.
* باید در عمل حذف و بازیابی محتوای فایل حفظ شود.
* در صورت عدم وجود فایلی برای بازیابی نباید هیچ اتفاقی بیفتد.
* برای حل این سوال می توانید هر فایل و یا پوشه دیگری که خواستید بسازید.
## نمونه
```
fm = FileManager()
fm.create_dir('test', '.')
fm.create_dir('test1', 'test')
fm.create_dir('test2', 'test/test1/')
fm.create_file('test.txt', 'test')
fm.create_file('test.txt', 'test/test1')
fm.create_file('test.txt', 'test/test1/test2')
```
#### خروجی: باید پوشه ها و فایل های زیر ایجاد شوند.
```
test
├── test1
│ ├── test2
│ │ └── test.txt
│ └── test.txt
└── test.txt
```
#### ادامه دستورات
```
print(fm.find('test.txt', 'test'))
```
#### خروجی
```
['test/test.txt', 'test/test1/test.txt', 'test/test1/test2/test.txt']
```
#### ادامه دستورات
```
fm.delete('test.txt', 'test')
fm.delete('test.txt', 'test/test1/')
fm.delete('test.txt', 'test/test1/test2')
fm.restore('test.txt')
fm.restore('test.txt')
```
#### خروجی
```
test
├── test1
├── test2
│ └── test.txt
└── test.txt
```
#### آنچه باید آپلود کنید:
یک فایل Zip شامل یک فایل به نام `source.py` که کلاس `FileManager` در آن قرار دارد آپلود کنید.
مدیریت فایلها
میخواهیم کلاسی در زبان پایتون طراحی کنیم که عملکرد یک پروکسی را برای اشیائی که نمایندهٔ آنان است داشته باشد. دقت کنید که این کلاس باید قادر باشد تمامی توابعی را که روی شی اصلی فراخوانی میشود، بدون تغییر کارکرد روی آنان اجرا کند. توجه کنید که این شئ میتواند از هر نوع کلاسی باشد.
فایل اولیه پروژه را از [اینجا](http://s8.picofile.com/file/8339068900/prox_initial.zip.html) دریافت کنید.
برای مثال اگر کد زیر را برای کلاس `Radio` داشته باشیم:
```python
class Radio():
def __init__(self):
self._channel = None
self.is_on = False
self.volume = 0
@property
def channel(self):
return self._channel
@channel.setter
def channel(self, value):
self._channel = value
def power(self):
self.is_on = not self.is_on
```
سپس اگر یک پروکسی از شئ از کلاس `Radio` بسازیم
```python
radio = Radio()
radio_proxy = Proxy(radio)
```
تمامی توابع بالا باید بر روی `radio_proxy` قابل فراخوانی باشد:
```python
radio_proxy.channel = 95
radio_proxy.power()
```
```
>>> radio.channel
>95
```
اگر تابع فراخوانیشده، جزو توابع تعریفشده برای شئ هدف نباشد، باید استثنایی با پیام `No Such Method` پرتاب شود.
همچنین کلاس `Proxy` که باید توابع آن را پیادهسازی کنید، باید قادر باشد اطلاعاتی را نیز ذخیرهسازی کند. توابع و ویژگیهایی که این کلاس باید دارا باشد به شرح زیر است: (این فایل اولیه را می توانید از اینجا دریافت کنید.)
```python
class Proxy:
def __init__(self, obj):
self._obj = obj
def last_invoked_method(self):
pass
def count_of_calls(self, method_name):
pass
def was_called(self, method_name):
pass
```
که در آن تابع `last_invoked_method` نام آخرین متد معتبر فراخوانی شده بر روی شئ هدف را بازمیگرداند و تایپ خروجی آن باید رشته باشد. در صورتی که تا لحظهٔ فراخوانی این تابع، متدی روی شئ فراخوانی نشده بود، استثنایی با پیام `No Method Is Invoked` پرتاب میشود.
تابع `count_of_calls` میبایست به ازای نام متد ورودی، تعداد دفعاتی که این متد بر روی شئ فراخوانی شده را بازگرداند. اگر متد ورودی معتبر نباشد، مقدار `0` را برمیگرداند.
تابع `was_called` به ازای نام متد معتبر ورودیَ، یک `Boolean` را به عنوان خروجی برمیگرداند که اگر آن متد برروی شئ فراخوانی شده باشد مقدار `True` و در غیر این صورت مقدار `False`را برمیگرداند. اگر متد ورودی معتبر نباشد مقدار `False` را برمیگرداند.
در نهایت توجه کنید که در صورتی که ویژگیها و متدهایی به شئ هدف، پس از ساختن پروکسی اضافه شوند، شئ پروکسی باید قادر به مدیریت توابع و ویژگیهای جدید نیز باشد.
پاسخ ارسالی شما، باید یک فایل زیپ با ساختار زیر باشد:
```
<Your-zip-file-name.zip>
└── Proxy.py
```
پروکسی
میخواهیم یک فایل HTML را پردازش کرده و همچنین اطلاعاتی را از آن استخراج کنیم. برای این کار، باید کلاسی به نام `HTMLParser` طراحی کنید که ویژگیها و توابع زیر را دارا باشد:
فایل اولیه پروژه را از [اینجا](http://s9.picofile.com/file/8339068418/html_parser_initial.zip.html) دریافت کنید.
```python
class HTMLParser:
def __init__(self, html_doc):
self.html_doc = html_doc
pass
def set_html_doc(self, html_doc):
self.html_doc = html_doc
def find_first(self, output_arg, **finding_args):
pass
def find_all(self, n, output_arg, **finding_args):
pass
def find_parent(self, output_arg, **finding_args):
pass
def find_grandparent(self, n, output_arg, **finding_args):
pass
def remove_comment(self, **finding_args):
pass
def remove_all_comments(self):
pass
def remove_tag(self, **finding_args):
pass
if __name__ == '__main__':
pass
```
پیش از شرح توابع و ویژگیها، مشخصههای جستجو `finding_args` و مشخصهٔ خروجی `output_arg` را شرح میدهیم:
۱- `finding_args`: یک دیکشنری است که میتواند یک یا چند کلید از میان `name` ، `id` ، `string` و `class` بپذیرد. برای مثال برای تگی مانند مثال زیر:
```html
<p class="important" id="some-ID">you can <i>access</i> Quera <a href="http://www.quera.ir">here</a>!</p>
```
مشخصهٔ `name` برابر `p`، مشخصهٔ `id` برابر `some-ID`، مشخصهٔ `string` برابر `you can access Quera here!` و مشخصهٔ `class` برابر `important` میباشد. **تضمین میشود** مشخصههای جستجوی ورودی تنها شامل مشخصههای فوق میباشد.
۲- `output_arg`: یک `str` است که مشخصهٔ مطلوب خروجی تابع را مشخص میکند و **تضمین میشود** مقادیری همانند بالا دارد. در صورتی که مقدار متناظر مشخصهٔ خروجی مقداردهی نشده بود، خروجی یک رشتهٔ خالی خواهد بود.
```python
parser = HTMLParser("<b>some text.</b>")
>>> parser.find_first("class", name='b')
>""
>>> parser.find_first("string", name='b')
>"some text."
>>> parser.find_first("name", string="some text.")
>'b'
```
برای مثال فراخوانی یک تابع با مشخصههای فوق چنین است:
```python
parser.find_first("string", id="link", name='a')
```
ویژگیها و توابع این کلاس به شرح زیر هستند:
۱- ویژگی `html_doc`: کل کد `HTML` به فرمت یک رشته `str` است.
۲- متد `find_first`: مقدار مشخصهٔ خروجی (`output_arg`) اولین برچسب که با ویژگیهای `finding_args` مطابقت داشته باشد را خروجی میدهد.
۳- متد `find_all`: یک لیست به طول **حداکثر** `n` از مقدار مشخصههای خروجی برچسبهای مطابق با `finding_args` را برمیگرداند (`n`تای اول). اگر تعداد برچسبهای یافتهشده کمتر از `n` بود لیستی به اندازهٔ همان تعداد برمیگرداند.
مثال:
```python
doc = '<b id="1">first</b> <b id="2">second</b> <b>third</b>'
parser.set_html_doc(doc)
>>> parser.find_all(4, "id", name='b')
>['1','2',""]
```
۴- متد `find_parent`: مقدار مشخصهٔ خروجی اولین والد برچسب مطابق با مشخصههای جستجو را خروجی میدهد. (در صورتی که بیش از یک برچسب یافت شد، مقدار مطلوب را برای اولین برچسب یافتهشده خروجی میدهد.) **تضمین میشود** برچسبهای یافتهشده حداقل یک والد دارند.
مثال:
```python
parser.set_html_doc("<p><b>example</b></p>")
>>>parser.find_parent("name", name='b', string='example')
>'p'
```
۵- متد `find_grandparent`: مقدار مشخصهٔ خروجی `n`اُمین جد (اولین جد همان والد است.) اولین برچسب مطابق با مشخصههای جستجو را خروجی میدهد. در صورتی که `n` از تعداد جدهای برچسب یافته شده بیشتر باشد، استثنایی با پیام `No Such Parent` پرتاب شود. **تضمین میشود** برچسبهای یافتهشده حداقل یک والد دارند.
۶- متد `remove_comment`: اولین برچسب منطبق با مشخصهٔ ورودی را در صورتی که **فقط** محتوی کامنت باشد به طور کلی از `html_doc`حذف میکند. کامنتهای `HTML` به صورت زیر هستند:
```html
<!--Some Comments-->
```
```python
parser.set_html_doc('<b id="comment"><!--comment--></b>')
>>> parser.remove_comment(name='b')
>>> parser.html_doc
>""
>>> parser.set_html_doc("<b>not a <!--comment--></b>")
>>> parser.remove_comment(name='b')
>>>parser.html_doc
>"<b>not a <!--comment--></b>"
```
در صورتی که محتویات اولین برچسب یافتهشده عاری از کامنت بود، استثایی با پیام `No Comments Found` پرتاب شود.
۷- متد `remove_all_comments`: تمامی برچسبهای محتوی کامنت را از `html_doc` حذف میکند.
۸- متد `remove_tag`: اولین برچسب مطابق با مشخصات ورودی را به طور کلی از `html_doc` حذف میکند.
### تذکر
در تمامی متدهایی که میبایست برچسبی مطابق با مشخصات ورودی یافت شود، اگر برچسبی یافت نشد باید استثنایی با پیام `No Such Tag` پرتاب شود.
## پاسخ ارسالی
در پاسخ ارسالی خود میتوانید از هر کتابخانهای که لازم میدانید استفاده کنید. اما در فایل ارسالی خود، علاوه بر فایل `HTMLParser.py` که حاوی راهحل شما است، باید فایلی با نام `python_requirements.txt` نیز موجود باشد که در آن نام کتابخانههای مورد نیاز و شمارهٔ نسخهٔ آنها به فرمت زیر در آن موجود باشد:
```
firstlib==1.2.3
secondlib==4.5.6
...
```
## ساختار فایل ارسالی
ساختار فایل `.zip` ارسالی شما باید به صورت زیر باشد:
```
<your_zip_file_name.zip>
├── HTMLParser.py
└── python_requirements.txt
```