برگزارکنندگان المپیک میخواهند سامانهای درست کنند که بهراحتی بتوانند اخبار برگزاری و وقایع هر رشته را به خبرگزاریهای مختلف ورزشی اطلاع دهند؛ به این صورت که یک سری پخشکنندهی اخبار دارند که وقایع را منتشر میکنند و یک سری دنبالکنندهی اخبار دارند که بهمحض دریافت خبر، آن را در خبرگزاریشان قرار میدهند. آنها از شما کمک خواستهاند تا در پیادهسازی این سامانه کمک کنید.
# پیادهسازی
شما باید سه کلاس `OlympicsServer`، `Publisher` و `Subscriber`را پیادهسازی کنید و همچنین توابع مشخصشدهی آنها را نیز پیادهسازی کنید. در صورت لزوم میتوانید توابع دیگری به کلاسهایتان اضافه کنید.
# تعاریف
### موضوع (topic)
هر خبری که منتشر میشود، یک موضوع دارد. بهطور مثال، `sports/basketball/groupstage` یک موضوع است؛ یعنی این خبر یک خبر ورزشی در رشتهی **بسکتبال** و در **دور گروهی** مسابقات است.
### پیام (message)
هر خبر یک پیام به همراه خود دارد. بهطور مثال، `Football match result: Team A won!` پیامی است که منتشرکننده آن را انتشار میدهد. پیامها بهصورت یک رشته (`string`) هستند.
### کیفیت سرویس (QoS)
کیفیت سرویس یعنی خبری که منتشر میشود، برای منتشرکننده چه میزان اهمیت دارد که به دست دنبالکنندهاش برسد. بهطور مثال، کیفیت سرویس سطح صفر یعنی برای منتشرکننده هیچ اهمیتی ندارد که پیامش به دنبال کنندهاش رسیده یا نه؛ یعنی پس از انتشار آن را فراموش میکند. ولی کیفیت سرویس سطح یک یعنی برای منتشر کننده مهم است که حداقل پیامش به یک مخاطبش رسیده باشد و رسیدن پیام را حداقل به یک دنبالکننده گارانتی میکند.
### منتشرکننده (publisher)
منتشرکننده یک کاربر *(client)* است که خبر را در یک موضوع مشخص، با یک پیام مشخص و یک کیفیت سرویس منتشر میکند. به طور مثال:
```python
client.publish('sports/handball', 'Match result: Team A won!', qos=1)
```
### دنبالکننده (subscriber)
دنبالکننده نیز یک کاربر است که یک موضوع مشخص را دنبال میکند و در صورت *match* شدن موضوعات، تابع `callback` را صدا میزند. بهطور مثال:
```python
client.subscribe('sports/football')
```
### تابع `callback`
تابع `callback` تابعی است که دنبالکننده پس از اینکه موضوع یک خبر با آن *match* میشود، آن تابع را صدا میزند. این تابع بهطور مثال میتواند پیام را چاپ کند یا در دیتابیس یا یک لیست اضافه کند. بهطور مثال این یک تابع `callback` است که پیام را صرفاً چاپ میکند:
```python
def message_callback(topic, message):
print(f"Received message on topic {topic}: {message}")
```
توجه داشته باشید که پیادهسازی این تابع را نیاز نیست انجام دهید و صرفا باید آن را هنگام *match* شدن تاپیک صدا بزنید.
### سطوح دسترسی موضوعات
- دسترسی صفرسطحی: در این دسترسی دنبالکنندگان باید کاملاً با تاپیک منطبق باشند. (بهطور مثال `/sports` به `sports/football` دسترسی ندارد.)
- دسترسی یکسطحی: در این دسترسی دنبالکنندگان تنها میتوانند در یک عمق با تاپیک اشتراک داشته باشند. (بهطور مثال `sports/+` به `sports/football` دسترسی دارد ولی به `sports/football/finals` دسترسی ندارد.)
- دسترسی چندسطحی: در این دسترسی دنبالکنندگان در چند عمق میتوانند با تاپیک اشتراک داشته باشند. (بهطور مثال `sports/#` به `sports/football` دسترسی دارد و همچنین به `sports/football/finals` نیز دسترسی دارد.)
### سرور
سرور به صورت چندنخی *(multi-thread)* کار میکند و هر خبر در یک نخ منتشر میشود. همچنین برای جلوگیری از *race condition* یک قفل *(lock)* در درون سرور قرار دارد که در جای لازم نخها را قفل میکند و همچنین دارای دو تابع اصلی `publish` و `subscribe` است. این دو تابع اکثر منطق برنامه را در خود جای میدهند و کلاس `Publisher` و `Subscriber` بهترتیب در خود `publish` و `subscribe` دارند که با استفاده از توابع متناظر خود در سرور عمل انتشار و دنبال کردن را انجام میدهند.
همچنین به این توجه داشته باشید که سرور باید برای دنبالکنندههایی که در حال حاضر آنلاین نیستند پیام را ذخیره کند. تاپیکها و پیامشان را به صورت آرایهای از تاپل و در فایل `messages.pkl` و بهصورت فایل `pickle` ذخیره کنید.
### کاربر (client)
این کلاس برای شما پیادهسازی شده است که صرفاً در توابع `publish` و `subscribe` با ساختن یک کلاس متناظر با آن و صدا کردن تابعش عمل انتشار و دنبال کردن خبر را انجام میدهد.
# مثال
## کد نمونه
```python
def message_callback(topic, message):
print(f"Received message on topic {topic}: {message}")
server = OlympicsServer()
client = Client(server)
client.subscribe('sports/#', message_callback)
client.publish('sports/football', 'Football match result: Team A won!', qos=1)
client.publish('sports/football/worldcup', 'World Cup result: Team Z won!', qos=1)
client.publish('sports/basketball/nba', 'NBA result: Team Y won!', qos=1)
```
## خروجی نمونه
```
Received message on topic sports/football: Football match result: Team A won!
Received message on topic sports/football/worldcup: World Cup result: Team Z won!
Received message on topic sports/basketball/nba: NBA result: Team Y won!
```
در اینجا دنبالکننده چون موضوع `sports/#` را دنبال کرده است، پس هر خبری که با `sports` شروع شود را دریافت و با صدا زدن تابع `callback` آن را چاپ میکند.
# پروژهی اولیه
پروژهی اولیهی این سؤال را میتوانید از [این لینک](/contest/assignments/71157/download_problem_initial_project/251417/) دانلود کنید.
# نحوهی ارسال
پس از تکمیل پروژه، فایلهای `client.py` و `server.py` را به صورت یک فایل `zip` را ارسال نمایید.
```
[your-zip-file-name].zip
├── client.py
└── server.py
```
ارسال پاسخ برای این سؤال
در حال حاضر شما دسترسی ندارید.