محمد، مهدی و پارسا به مسابقات ACM جهانی راهیافتهاند روز مسابقه هر تیم باید نام تیم خود را در فایل دیتابیس sqlite اضافه کند. ما باید کتابخانهای بنویسیم که کار را برای ارتباط با دیتابیس برای مهدی و دوستان ساده کند. این کد قرار است شبیه به یک [ORM](https://en.wikipedia.org/wiki/Object-relational_mapping) کار کند.
در این سوال تنها کتابخانه sqlalchemy نصب شده است که میتوانید از آن استفاده کنید.
حال از شما خواسته شده که کلاس `ORM` را به صورت خواسته شده پیادهسازی کنید.
```python orm.py
from exceptions import *
class ORM:
def __init__(self, db_path):
self.db_path = db_path
self.create_database_if_not_exist()
def create_database_if_not_exist(self):
pass
def create_table(self, table_path, table_name):
pass
def add_value(self, table_path, table_name, **values):
pass
def add_values(self, table_path, table_name, values):
pass
def remove_value(self, table_path, table_name, **values):
pass
def remove_values(self, table_path, table_name, values):
pass
def get_values(self, table_path, table_name, **filters):
pass
```
<details class="green">
<summary> **تابع** `create_database_if_not_exist` </summary>
در صورتی که فایل پایگاه داده تاکنون ساخته نشده آن را در آدرس `db_path` میسازد. نمونه `db_path` که به پوشه databases و پایگاه first.sqlite3 اشاره میکند (پوشه databases از قبل باید وجود داشته باشد):
`db_path = r'databases/first.sqlite3''`
</details>
<details class="green">
<summary> **تابع** `create_table` </summary>
قرار است مسیر تعریف `table` و نام آن را دریافت کند و جدول را در پایگاه داده بسازد.
نحوه تعریف جدول در پایگاه داده به صورت زیر است.
```python users.py
# path : tables/users.py
from sqlalchemy import Integer, Column, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = 'user'
name = Column(String, primary_key=True)
family = Column(String, primary_key=True)
likes = Column(Integer)
order_fields = ['name', 'family']
def to_json(self):
return {
'name': self.name,
'family': self.family,
'likes': self.likes
}
```
در این صورت تابع را به صورت `create_table('tables.user', 'User')` صدا میزنیم. در صورت موجود بودن جدول در پایگاه داده، باید استثنای `TableExistsException` را `raise` کنید.
</details>
<details class="green">
<summary> **تابع** `add_value` </summary>
مقدار جدید را میگیرد و در جدول مربوطه ذخیره میکند. نحوه صدا زدن این تابع به صورت
`add_value('tables.user', 'User', name='mahdi', family='shokri', likes=3)`
است. در صورت موجود بودن مقدار از قبل در جدول باید استثنای `ValueExistException` و در صورت هرگونه مشکل دیگر باید استثنای `AddValueException` را `raise` کنیم.
</details>
<details class="green">
<summary> **تابع** `add_values` </summary>
مقادیر جدید را میگیرد و در جدول مربوطه ذخیره میکند. نحوه صدا زدن این تابع به صورت
`add_values('tables.user', 'User', [{'name': 'mahdi', 'likes': 3, 'family': 'shokri' }])`
است. به ازای هر مورد در آرایه آن را به جدول اضافه میکند. توجه کنید که یا همه موارد اضافه شوند یا در صورت هرگونه مشکل برای هرکدام از موارد آرایه، باید استثنا `AddValuesException` را `raise` کنیم و پایگاه داده را به حالت قبل از این دستور، `rollback` کنیم.
</details>
<details class="green">
<summary> **تابع** `remove_value` </summary>
مقداری را میگیرد و بر اساس آرگومانها موارد را فیلتر و حذف میکند. مثلا
`add_value('tables.user', 'User', name='mahdi')`
تمام افرادی که نامشان `mahdi` است را حذف میکند. در صورت نبودن سطری برای حذف شدن در جدول یا هرگونه مشکل دیگر باید استثنای `RemoveValueException` را `raise` کند.
</details>
<details class="green">
<summary> **تابع** `remove_values` </summary>
آرایهای از فیلترها میگیرد و به ازای هر کدام از فیلترها، موارد مربوط به آن فیلتر را که در جدول مطابقت دارند حذف میکند.
`add_value('tables.user', 'User', [{'name': 'mahdi'}, {family': 'mohammadi'}])`
به ازای هر کدام از موارد در آرایه حداقل باید یک سطر برای حذفشدن وجود داشته باشد در غیر این صورت استثنای `RemoveValuesException` بازگردانده شود.
</details>
<details class="green">
<summary> **تابع** `get_values` </summary>
مقادیر فیلتر را دریافت میکند و نتیجهای را به صورت `json` باز میگرداند. مثلا اگر تابع
`get_values('tables.user', 'User', name='mahdi')`
را صدا بزنیم تمام سطرهای جدول که `name` در آنها `mahdi` باشد را برمیگرداند. توجه کنید که ترتیب موارد در خروجی `json` باید بر اساس `order_fields` در تعریف جدول (در اینجا `User`) باشد.
```json
[
{
'name': 'mahdi',
'family': 'shokri',
'likes': 4
},
{
'name': 'mahdi',
'family': 'sorkhi',
'likes': 2
}
]
```
**توجه**: هر جدولی که برای تست به شما کد شما داده شود، حتما دارای تابع `to_json()` است و نیازی نیست شما آن را تبدیل به json کنید.
</details>
## نکات
+ میتوانید فایل اولیه این سوال را از [این لینک](/problemset/assignments/4367/download_problem_initial_project/16398/) دریافت کنید.
+ تابع `get_value` نباید مقداری را چاپ کند، بلکه باید مقادیر مورد نظر را بازگرداند.
+ شما میتوانید با پیادهسازی این سوال به صورت بخشبخش نمره بگیرید.
+ در صورت تمایل، میتوانید علاوهبر کلاس خواستهشده، کلاسها و تابعهای کمکی دیگری نیز در فایل `orm.py` پیادهسازی کنید.
## نحوه ارسال
شما باید کلاس `ORM` موجود در فایل `orm.py` را تکمیل و سپس این فایل را ارسال کنید.