چاپ جمع


  • محدودیت زمان: ۱ ثانیه
  • محدودیت حافظه: ۲۵۶ مگابایت

حسینقلی به تازگی جمع را یاد گرفته است. او یک عادت عجیب دارد، هنگامی که در دفتر خود عدد nn را ببیند، روی تخته، اعداد ۱ تا nn را به ترتیب می‌نویسد، سپس آن‌ها را جمع می‌زند و حاصل را به صورت زیر روبه‌روی آن‌ها می‌نویسد. 1+2+3+...+n=k=1nk 1 + 2 + 3 + ... + n = \sum_{k=1}^n k

برای مثال اگر بر روی دفتر خود عدد ۳ را ببیند روی تخته عبارت زیر را می‌نویسد: 1+2+3=6 1 + 2 + 3 = 6 و یا اگر عدد ۱ را ببیند عبارت زیر را می‌نویسد: 1=1 1 = 1

با گرفتن عددی که حسینقلی بر روی دفترش دارد، عبارتی را که بر روی تخته می‌نویسد، پیش‌بینی کنید!

ورودی🔗

در تنها خط ورودی، عدد صحیح و مثبت nn به شما داده می‌شود.

1n1000 1 \le n \le 1000

خروجی🔗

در تنها خط خروجی، عبارتی را که حسینقلی روی تخته نوشته است، به طور کامل چاپ کنید.

مثال‌ها🔗

ورودی نمونه ۱🔗

5
Plain text

خروجی نمونه ۱🔗

1 + 2 + 3 + 4 + 5 = 15
Plain text

ورودی نمونه ۲🔗

1
Plain text

خروجی نمونه ۲🔗

1 = 1
Plain text
اشتباهات متداول
چک کردن شرایط ورودی مسئله

نیازی به چک کردن برقراری شرایط گفته‌شده در ورودی نیست. توضیحات محدودیت‌ها فقط برای آگاهی شما درباره‌ی تست‌ها و محدودیت‌های مسئله است و قطعاً در ورودی‌های داده‌شده به برنامه‌ی شما رعایت می‌شوند. پس نیازی نیست بنویسید:

if 1 <= n <= 100:
    # answer of problem
else:
    # print('invalid input')
Python
ابتدا همه‌ی ورودی را گرفتن و در نهایت همه‌ی خروجی را چاپ کردن

شما می‌توانید لابه‌لای دریافت ورودی، خروجی دهید. پس نیازی نیست ابتدا همه‌ی ورودی‌ها را دریافت کنید و در نهایت همه‌ی خروجی‌ها را چاپ کنید. مخصوصاً برای سوالاتی که باید به چندین سوال پاسخ دهید، می‌توانید دو قسمت ورودی و خروجی را کاملاً مستقل در نظر بگیرید و مطمئن باشید تداخلی پیش نمی‌آید.

چاپ کردن موارد اضافه برای دریافت ورودی

لطفاً از چاپ کردن موارد اضافه مثل please enter a number برای دریافت ورودی پرهیز کنید. برای مثال در زبان پایتون نباید بنویسید:

input('please enter:')
Python
چند فایلی کد زدن

برای زبان‌هایی مثل جاوا نباید در بالای کد شما آدرس پکیج داده شود. برای مثال در بالای کد خود نباید بنویسید:

package ir.quera.contest;
Java
استفاده از چند Scanner برای دریافت ورودی

در زبان جاوا، باید فقط یک شئ از جنس Scanner تعریف کنید و همه‌ی ورودی‌ها را با آن دریافت کنید.

نام فایل ارسالی در جاوا

در زبان جاوا، باید نام فایل ارسالی شما با نام کلاسی که تابع main در آن قرار دارد یکسان باشد، برای مثال اگر نام کلاس شما Question1 است، نام فایل ارسالی شما باید Question1.java باشد.

نحوه‌ی دریافت ورودی و چاپ کردن خروجی

برای آشنایی بیشتر برای نحوه‌ی دریافت ورودی و چاپ کردن خروجی این لینک را مطالعه کنید.

رشته‌ی خوب و رشته‌ی بد


  • محدودیت زمان: ۱ ثانیه
  • محدودیت حافظه: ۲۵۶ مگابایت

حسینقلی دو رشته‌ی ss و tt دارد که هر دوی‌ آن‌ها از حروف کوچک انگلیسی تشکیل شده‌اند. او رشته‌ی ss را بسیار دوست دارد و از رشته‌ی tt بسیار متنفر است. او به دنبال یک رشته به طول nn است به طوری که این رشته از حروف کوچک انگلیسی تشکیل شده باشد و رشته‌ی ss زیررشته‌ی آن باشد و رشته‌ی tt زیررشته‌ی آن نباشد.

به او کمک کنید چنین رشته‌ای را پیدا کند و یک مثال درست را خروجی دهید و اگر چنین رشته‌ای وجود ندارد عبارت 1-1 را خروجی دهید.

  • رشته‌ی aa زیررشته‌‌ی رشته‌ bb است اگر و تنها اگر یک بازه‌ی متوالی از رشته‌ی bb وجود داشته باشد که برابر رشته‌ی aa باشد.

ورودی🔗

در خط اول ورودی، رشته‌ی ss ورودی داده می‌شود. در خط دوم ورودی، رشته‌ی tt ورودی داده می‌شود. و در خط سوم عدد صحیح nn ورودی داده می‌شود.

1s,t1000 1 \le |s|, |t| \le 1000 1n100 0001 \le n \le 100\ 000

خروجی🔗

در تنها خط خروجی یک رشته‌ خروجی دهید به طوری که شرط‌های گفته شده را داشته باشد. اگر چنین رشته‌ای وجود نداشت عبارت 1-1 را خروجی دهید.

مثال‌ها🔗

ورودی نمونه ۱🔗

code
hamkaran
7
Plain text

خروجی نمونه ۱🔗

hamcode
Plain text

ورودی نمونه ۲🔗

hamcode
code
10
Plain text

خروجی نمونه ۲🔗

-1
Plain text

نقشه‌کشی جدول


  • محدودیت زمان: ۱ ثانیه
  • محدودیت حافظه: ۲۵۶ مگابایت

یک ساختمان داریم که به شکل یک جدول n×mn \times m است. می‌دانیم دور تا دور این جدول دیوار است. می‌خواهیم تعدادی از این خانه‌ها را مسدود کنیم به طوری که تعداد ناحیه‌هایی که از خانه‌های مسدود نشده داخل جدول به‌وجود می‌آید دقیقا kk تا باشد. همچنین برای هدر نرفتن مساحت ساختمان، می‌خواهیم هیچ خانه‌ی مسدودی نداشته باشیم که همه‌ی همسایه‌های اطرافش مسدود باشد.

  • دو خانه از جدول همسایه هستند اگر در حداقل یک نقطه مشترک باشند.
  • دو خانه باهم در یک ناحیه قرار دارند اگر هر دو آن‌ها مسدود نشده باشند و همچنین از یک خانه به دیگری مسیری از خانه‌های همسایه و مسدود نشده وجود داشته باشد.

به شما nn و mm و kk داده می‌شود. اگر جدولی با خواسته‌های سؤال وجود داشت آن‌ را خروجی دهید در غیر این صورت عبارت 1-1 را خروجی دهید.

ورودی🔗

در یک خط به ترتیب سه عدد صحیح nn و mm و kk ورودی داده می‌شود.

1n,m1000 1 \le n, m \le 1000 1kn×m 1 \le k \le n \times m

خروجی🔗

اگر جدولی با خواسته‌های سؤال وجود داشت آن‌ را خروجی دهید در غیر این صورت -1 را خروجی دهید.

برای خانه‌های مسدود X و خانه‌های مسدود نشده از O استفاده کنید.

مثال‌ها🔗

ورودی نمونه ۱🔗

4 7 5
Plain text

خروجی نمونه ۱🔗

OXOXOXO
OXOXOXO
OXOXXXO
OXOXOXO
Plain text

ورودی نمونه ۲🔗

2 2 4
Plain text

خروجی نمونه ۲🔗

-1
Plain text

دزدی کلان در کلانتری


  • محدودیت زمان: ۱ ثانیه
  • محدودیت حافظه: ۲۵۶ مگابایت

حسینقلی که در کلانتری کار می‌کند و کل ساعت کاری را با یک دزدی کلان سر و کله زده است، بعد از یک روز طولانی به خانه بازگشته است. ناگهان بر روی تخته تعداد زیادی kalantar\text{kalantar} و kalan\text{kalan} و tar\text{tar} و اسپیس (فضاهای خالی) می‌بیند. با توجه به اینکه ممکن است این کار تهدید دزدها باشد، قرار است این متن را بررسی کند تا زمانی که بررسی به پایان برسد.

در هر مرحله از بررسی، حسینقلی به دنبال زیررشته‌های برابر با kalan\text{kalan} که دقیقاً بعد از آن‌ها و یا بعد از تعدادی فضای خالی tar\text{tar} نوشته شده باشد می‌گردد و سپس تمام این kalan\text{kalan}های پیدا شده با tar\text{tar}های بعد آن‌ها را با فضای خالی بینشان (در صورت وجود) همزمان پاک می‌کند.

برای مثال متن زیر در هر مرحله بررسی به متن خط بعد از آن تبدیل می‌شود، تا جایی که بعد از دو مرحله در ادامه‌ی هیچ زیر رشته kalan\text{kalan} رشته tar\text{tar} مشاهده نمی‌شود. kalan tar tartar kalankalantar tar kalan kalantar tar\text{kalan tar tartar kalankalantar tar kalan kalantar tar}  tartar kalan tar kalan tar\text{ tartar kalan tar kalan tar}  tartar \text{ tartar }

می‌دانیم هر کلانتری که حسینقلی پاک می‌کند یک واحد تهدید و هر کلان تر که تعدادی فضای خالی بین آن‌ها باشد یک واحد گمراهی است.

برای کمک به حسینقلی با گرفتن متن روی تخته، تعداد مراحل بررسی را چاپ کنید و در هر مرحله تعداد تهدیدها و گمراهی ها را به او هشدار دهید.

ورودی🔗

در تنها خط ورودی، رشته ss که نشاندهنده متن روی تخته است به شما ورودی داده می‌شود. 1s106 1 \le |s| \le 10^6

خروجی🔗

در خط اول خروجی، تعداد مراحل بررسی متن توسط حسینقلی را خروجی دهید.

سپس در خطوط بعدی، در هر خط به ترتیب تعداد تهدیدهای یک بررسی و سپس تعداد گمراهی‌های آن بررسی را خروجی دهید. مراحل بررسی را به ترتیب از مرحله اول تا آخر خروجی دهید.

مثال‌ها🔗

ورودی نمونه ۱🔗

kalankalan tar kalankalantar tar kalantartar tar
Plain text

خروجی نمونه ۱🔗

3
2 1
0 1
0 1
Plain text

متن اولیه نمونه ۱ به شرح زیر است: kalankalan tar kalankalantar tar kalantartar tar\text{kalankalan tar kalankalantar tar kalantartar tar} در مرحله اول زیررشته‌های kalantar، kalantar و kalan tarپاک می‌شوند. kalan kalan tar tar tar\text{kalan kalan tar tar tar} در مرحله دوم زیررشته‌ kalan tar پاک می‌شود. kalan tar tar\text{kalan tar tar} در مرحله سوم زیررشته‌ kalan tar پاک می‌شود.  tar\text{ tar}

گیرینُف


تیم همکاران‌سیستم تصمیم گرفته سیستم ردیابی ساعات ورود و خروج خود را با قابلیت شخصی‌سازی برای تیم HR طراحی کند. همکاران که به‌شدت شرکت منظمی است و روی نظم کارمندان خود نیز حساس است از شما می‌خواهد برای ورود و استخدام به این شرکت و کمک به تیم منابع انسانی، بک‌اند سیستم ترکینگ ورود و خروج برنامه‌نویسان برای تهیه گزارش‌ها و محاسبه حقوق‌شان را پیاده‌سازی کنید.

جزئیات پروژه🔗

پروژه اولیه را از این لینک دانلود کنید. ساختار پروژه به شکل زیر است:

.
├── app
│   ├── db.go
│   ├── handlers.go   # TODO Implement
│   ├── models.go     # TODO Implement
│   ├── repository.go # TODO Implement
│   └── server.go
├── go.mod
├── go.sum
├── main.go
├── test
│   └── sample_test.go
Shell

در این پروژه، شما باید قسمت‌های مشخص شده با // TODO را پیاده‌سازی کنید. در نهایت شما از طریق اجرای فایل باینری main.go می‌توانید پروژه خود را اجرا کنید.

مدل‌های پایگاه‌داده🔗

ساختار جداول پروژه به شرح زیر باشد:

1- جدول Programmer

Column Name Type Notes
ID uint PRIMARY KEY AUTO_INCREMENT
Name string Programmer's name
CreatedAt time.Time Creation timestamp
UpdatedAt time.Time Last update timestamp

2- جدول Attendance

Column Name Type Notes
ID uint PRIMARY KEY AUTO_INCREMENT
ProgrammerID uint Foreign key to Programmer table
Date time.Time Attendance date
CheckIn time.Time Check-in time
CheckOut time.Time Check-out time
CreatedAt time.Time Creation timestamp
UpdatedAt time.Time Last update timestamp

روابط🔗

روابط بین مدل‌ها باید به شرح زیر پیاده‌سازی شوند:

  • هر برنامه‌نویس می‌تواند دارای چندین رکورد ورود و خروج باشد.
  • هر رکورد ورود و خروج متعلق به یک برنامه‌نویس است.
  • هر برنامه‌نویس در هر روز فقط باید یک رکورد ورود و خروج در سیستم ثبت نماید.

توابع Marshal/Unmarshal جیسون🔗

این توابع باید تبدیل صحیح بین JSON و ساختارهای Go برای مدل Attendance و AttendanceInput را مدیریت کنند:

func (a *Attendance) UnmarshalJSON(data []byte) error
func (a Attendance) MarshalJSON() ([]byte, error)
func (ai *AttendanceInput) UnmarshalJSON(data []byte) error
Go
متد UnmarshalJSON برای Attendance
func (a *Attendance) UnmarshalJSON(data []byte) error
Go
  • داده‌های JSON را به ساختار Attendance تبدیل می‌کند.

  • فیلدهای date، check_in و check_out از نوع رشته (string) دریافت می‌شوند و به نوع time.Time تبدیل می‌شوند.

  • فرمت‌های مورد انتظار:

    • date: YYYY-MM-DD

    • check_in: YYYY-MM-DD HH:MM:SS

    • check_out: YYYY-MM-DD HH:MM:SS

  • در صورت خطا در تبدیل فرمت‌ها، خطا برگردانده می‌شود.

خطاهای ممکن:

  • اگر date فرمت نامعتبر داشته باشد، خطای parsing time با پیام expected YYYY-MM-DD برگردانده می‌شود.

  • اگر check_in یا check_out فرمت نامعتبر داشته باشند، خطای parsing time با پیام expected YYYY-MM-DD HH:MM:SS برگردانده می‌شود.

مثال ورودی معتبر:

{
  "date": "2024-01-01",
  "check_in": "2024-01-01 09:00:00",
  "check_out": "2024-01-01 17:00:00",
  "programmer_id": 1
}
JSON

مثال ورودی نامعتبر:

{
  "date": "01-01-2024",
  "check_in": "09:00:00",
  "check_out": "2024-01-01 17:00:00",
  "programmer_id": 1
}
JSON
متد MarshalJSON برای Attendance
func (a Attendance) MarshalJSON() ([]byte, error)
Go
  • ساختار Attendance را به JSON تبدیل می‌کند.

  • فیلدهای date، check_in و check_out از نوع time.Time به رشته (string) تبدیل می‌شوند.

  • فرمت‌های خروجی:

    • date: YYYY-MM-DD در UTC

    • check_in: YYYY-MM-DD HH:MM:SS در UTC

    • check_out: YYYY-MM-DD HH:MM:SS در UTC

  • در صورت خطا در تبدیل، خطا برگردانده می‌شود.

مثال خروجی:

{
  "id": 1,
  "programmer_id": 1,
  "date": "2024-01-01",
  "check_in": "2024-01-01 09:00:00",
  "check_out": "2024-01-01 17:00:00"
}
JSON
متد UnmarshalJSON برای AttendanceInput
func (i *AttendanceInput) UnmarshalJSON(data []byte) error
Go
  • داده‌های JSON را به ساختار AttendanceInput تبدیل می‌کند.

  • فیلد date اجباری است و باید به فرمت YYYY-MM-DD باشد.

  • فیلدهای check_in و check_out اختیاری هستند و اگر وجود داشته باشند، باید به فرمت YYYY-MM-DD HH:MM:SS باشند.

  • در صورت خطا در تبدیل فرمت‌ها یا عدم وجود فیلد date، خطا برگردانده می‌شود.

خطاهای ممکن:

  • اگر date وجود نداشته باشد، خطای date field is necessary برگردانده می‌شود.

  • اگر date فرمت نامعتبر داشته باشد، خطای invalid date format, expected YYYY-MM-DD برگردانده می‌شود.

  • اگر check_in یا check_out فرمت نامعتبر داشته باشند، خطای invalid check_in/check_out format, expected YYYY-MM-DD HH:MM:SS برگردانده می‌شود.

مثال ورودی معتبر:

{
  "date": "2024-01-01",
  "check_in": "2024-01-01 09:00:00",
  "check_out": "2024-01-01 17:00:00"
}
JSON

مثال ورودی نامعتبر:

{
  "date": "2024/01/01",
  "check_in": "09:00:00"
}
JSON

نکته: برای یکتا بودن ProgrammerID و Date در Attendance می‌توانید از ایندکس کمک بگیرید.

هندلرها🔗

شما باید هندلرهای HTTP اندپوینت‌های زیر را با مشخصات دقیق آن‌ها که در ادامه بیان خواهد شد پیاده‌سازی کنید:

func RegisterRoutes(mux *http.ServeMux) {
    mux.HandleFunc("POST /attendance", CreateAttendanceHandler)
    mux.HandleFunc("GET /attendance/{programmer_id}", GetAttendanceHandler)
    mux.HandleFunc("PUT /attendance/{programmer_id}", UpdateAttendanceHandler)
    mux.HandleFunc("DELETE /attendance/{programmer_id}", DeleteAttendanceHandler)
    mux.HandleFunc("DELETE /attendance/{programmer_id}/{date}", DeleteOneDayAttendanceHandler)
    mux.HandleFunc("GET /attendance", GetAllAttendanceHandler)

    mux.HandleFunc("GET /girinof/{programmer_id}/{date}", GetGirinofReportHandler)
    mux.HandleFunc("GET /report/monthly/{programmer_id}/{start_date}/{end_date}", GetMonthlyReportHandler)
    mux.HandleFunc("GET /salary/{programmer_id}", GetSalaryHandler)

    mux.HandleFunc("POST /programmer", CreateProgrammerHandler)
}
Go
هندلر CreateAttendanceHandler

توضیح:

این هندلر برای ثبت حضور و غیاب برنامه‌نویسان است. ابتدا داده‌های ورودی را از ریکوئست دریافت کرده و اعتبار آن‌ها را بررسی می‌کند (مانند فرمت درست، وجود فیلد تاریخ، و ترتیب ورود و خروج). در صورت معتبر بودن، اطلاعات حضور را در سیستم ذخیره کرده و پاسخ مناسب ارسال می‌کند.

  • متد: POST
  • اندپوینت یا مسیر: /attendance
  • ورودی: بدنه JSON شامل:
{
  "programmer_id": uint,
  "date": "YYYY-MM-DD",
  "check_in": "YYYY-MM-DD HH:MM:SS",
  "check_out": "YYYY-MM-DD HH:MM:SS"
}
JSON

اعتبارسنجی:

  • در صورت دیکد نشدن JSON باید Invalid input format را با وضعیت 400 برگردانید.
  • در صورت عدم وجود فیلد تاریخ یا date باید Missing required field: date را با وضعیت 400 برگردانید.
  • در صورتی که ساعت خروج قبل از ساعت ورود بود، باید پیام CheckOut cannot be before CheckIn را با وضعیت 400 برگردانید.
  • در صورتی که programmer_id ارسال‌شده در پایگاه‌داده موجود نبود باید پیام Programmer not found! Please hire the programmer first. را با وضعیت 404 برگردانید.

پاسخ موفقیت‌آمیز:

  • وضعیت: 201 (ایجاد شده)
  • Content-Type: application/json
  • Body: شی attendance ایجاد شده

پاسخ خطا:

  • وضعیت: 500 با پیام Failed to create attendance: + پیام خطا (اگر رکوردی با همان programmer_id و date وجود داشته باشد، باید Duplicate attendance record را برگرداند که شما این مورد را در لایه‌ی ریپوزیتوری هندل می‌کنید)
هندلر GetAttendanceHandler

توضیح:

این هندلر برای دریافت سوابق حضور و غیاب یک برنامه‌نویس خاص است. ابتدا شناسه برنامه‌نویس را از مسیر URL استخراج کرده و اعتبارسنجی می‌کند. سپس، اگر داده‌ای موجود باشد، سوابق حضور را در قالب JSON برمی‌گرداند، در غیر این صورت پیام خطای مناسب ارسال می‌شود.

  • متد: GET

  • اندپوینت یا مسیر: /attendance/{programmer_id}

  • ورودی: programmer_id به عنوان پارامتر مسیر

اعتبارسنجی:

  • اگر programmer_id نامعتبر باشد (مثلاً عددی نباشد یا کمتر از ۱ باشد)، باید Invalid Programmer ID را با وضعیت 400 برگردانید.

  • اگر هیچ رکورد حضور و غیابی برای programmer_id داده‌شده وجود نداشته باشد، باید No attendance records found for the given Programmer ID را با وضعیت 404 برگردانید.

پاسخ موفقیت‌آمیز:

  • وضعیت: 200 (موفق)

  • Content-Type: application/json

  • Body: لیستی از رکوردهای حضور و غیاب مربوط به programmer_id

پاسخ خطا:

  • وضعیت: 500 با پیام Error retrieving attendance records + پیام خطا
هندلر UpdateAttendanceHandler

توضیح:

این هندلر برای به‌روزرسانی اطلاعات حضور و غیاب یک برنامه‌نویس است. ابتدا شناسه برنامه‌نویس و تاریخ موردنظر را بررسی کرده و در صورت وجود، اطلاعات جدید (مانند ورود و خروج) را ثبت می‌کند. اگر داده‌ای نامعتبر باشد یا رکورد حضور قبلاً ثبت نشده باشد، پیام خطای مناسب ارسال می‌شود.

  • متد: PUT

  • اندپوینت یا مسیر: /attendance/{programmer_id}

  • ورودی: بدنه JSON دو فیلد ورود و خروج به صورت اختیاری:

{
  "date": "YYYY-MM-DD",
  "check_in": "YYYY-MM-DD HH:MM:SS",
  "check_out": "YYYY-MM-DD HH:MM:SS"
}
JSON

اعتبارسنجی:

  • اگر programmer_id نامعتبر باشد (مثلاً عددی نباشد یا کمتر از ۱ باشد)، باید Invalid Programmer ID" را با وضعیت 400 برگردانید.

  • اگر programmer_id در دیتابیس وجود نداشته باشد، باید Programmer not found را با وضعیت 404 برگردانید.

  • اگر فیلد date یا programmer_id ارسال نشوند باید پیام Programmer ID is required و Date is required با وضعیت 400 برگردانید.

  • اگر date ارسال‌شده برای programmer_id موجود نباشد، باید Programmer with the given date not recorded. Please use CreateAttendanceHandler to create it! را با وضعیت 400 برگردانید.

  • اگر تاریخ خروج قبل از تاریخ ورود باشد، باید CheckIn cannot be after CheckOut را با وضعیت 400 برگردانید.

پاسخ موفقیت‌آمیز:

  • وضعیت: 200 (موفق)
  • Content-Type: application/json
  • Body: گزارش GirinofReport به شکل زیر:
{
  "programmer_id": "1",
  "total_delays": 2,
  "total_early_departures": 3
}
JSON

پاسخ خطا:

  • وضعیت: 500 با پیام Failed to update attendance + پیام خطا
هندلر DeleteAttendanceHandler

توضیح:

این هندلر برای حذف سوابق حضور و غیاب یک برنامه‌نویس است. ابتدا شناسه برنامه‌نویس را بررسی کرده و در صورت معتبربودن، رکورد یا رکوردهای حضور مربوطه را حذف می‌کند. در صورت نامعتبربودن شناسه یا بروز خطا، پیام خطای مناسب ارسال می‌شود.

  • متد: DELETE

  • اندپوینت یا مسیر: /attendance/{programmer_id}

  • ورودی: programmer_id به عنوان پارامتر مسیر

اعتبارسنجی:

  • اگر programmer_id نامعتبر باشد (مثلاً عددی نباشد یا کمتر از ۱ باشد)، باید Invalid programmer ID را با وضعیت 400 برگردانید.

  • اگر برنامه‌نویسی با آیدی متناظر با programmer_id موجود نباشد، باید Programmer not found را با وضعیت 404 برگردانید.

  • اگر date در قالب YYYY-MM-DD نباشد، باید Invalid date format را با وضعیت 400 برگرداند. (این مورد را در بخش توابع Marshal/Unmarshal جیسون هندل می‌کنید)

پاسخ موفقیت‌آمیز:

  • وضعیت: 202 (پذیرفته‌شده)

پاسخ خطا:

  • وضعیت: 500 با پیام Failed to delete attendance + پیام خطا
هندلر DeleteOneDayAttendanceHandler

توضیح:

این هندلر برای حذف سوابق حضور و غیاب یک برنامه‌نویس در یک تاریخ مشخص است. ابتدا شناسه برنامه‌نویس و تاریخ را از مسیر URL دریافت کرده و بررسی می‌کند. در صورت معتبربودن و وجود رکورد، آن را حذف می‌کند؛ در غیر این صورت، پیام خطای مناسب ارسال می‌شود.

  • متد: DELETE

  • اندپوینت یا مسیر: /attendance/{programmer_id}/{date}

  • ورودی:

    • programmer_id به عنوان پارامتر مسیر

    • date به عنوان پارامتر مسیر (فرمت: YYYY-MM-DD)

اعتبارسنجی:

  • اگر programmer_id نامعتبر باشد (مثلاً عددی نباشد یا کمتر از ۱ باشد)، باید Invalid programmer ID را با وضعیت 400 برگردانید.

  • اگر date فرمت نامعتبر داشته باشد، باید Invalid Date را با وضعیت 400 برگردانید.

  • اگر programmer_id یا date مورد نظر وجود نداشت، باید No attendance record found for the given date با وضعیت 404 را بازگردانید.

پاسخ موفقیت‌آمیز:

  • وضعیت: 202 (پذیرفته‌شده)

پاسخ خطا:

  • وضعیت: 500 با پیام Failed to delete attendance + پیام خطا
هندلر GetAllAttendanceHandler

توضیح:

این هندلر برای دریافت تمامی سوابق حضور و غیاب است. ابتدا تمام داده‌های حضور را از پایگاه‌داده واکشی کرده و در قالب JSON بازمی‌گرداند. در صورت بروز خطا در دریافت اطلاعات، پیام خطای مناسب ارسال می‌شود.

  • متد: GET

  • اندپوینت یا مسیر: /attendance

  • ورودی: ندارد

اعتبارسنجی:

  • نیاز به اعتبارسنجی خاصی ندارد (همه رکوردها را برمی‌گرداند)

پاسخ موفقیت‌آمیز:

  • وضعیت: 200 (موفق)

  • Content-Type: application/json

  • Body: لیست کامل تمامی رکوردهای حضور و غیاب

پاسخ خطا:

  • وضعیت: 500 با پیام Failed to fetch attendance records + پیام خطا
هندلر GetGirinofReportHandler

توضیح:

این هندلر برای دریافت گزارش حضور و غیاب یک برنامه‌نویس در یک تاریخ مشخص است. ابتدا شناسه برنامه‌نویس و تاریخ را بررسی کرده و در صورت وجود اطلاعات، گزارش را تولید و در قالب JSON بازمی‌گرداند. در صورت عدم وجود داده یا خطای پردازش، پیام خطای مناسب ارسال می‌شود.

  • متد: GET

  • اندپوینت یا مسیر: /girinof/{programmer_id}/{date}

  • ورودی:

    • programmer_id به عنوان پارامتر مسیر

    • date به عنوان پارامتر مسیر (فرمت: YYYY-MM-DD)

اعتبارسنجی:

  • اگر programmer_id ارسال‌ نشده باشد، باید Programmer ID is required را با وضعیت 400 برگرداند.
  • اگر date ارسال‌ نشده باشد، باید Date is required را با وضعیت 400 برگرداند.
  • اگر date فرمت نامعتبر داشته باشد، باید Invalid Date را با وضعیت 400 برگرداند.
  • اگر programmer_id عدد نباشد یا کمتر از ۱ باشد، باید Invalid Programmer ID را با وضعیت 400 برگرداند.
  • اگر برنامه‌نویس با programmer_id داده‌شده وجود نداشته باشد، باید Programmer not found را با وضعیت 404 برگرداند.
  • اگر رکورد حضور برای programmer_id و date داده‌شده وجود نداشته باشد، باید Programmer with the given date not recorded. Please use CreateAttendanceHandler to create it! را با وضعیت 400 برگرداند.

پاسخ موفقیت‌آمیز:

  • وضعیت: 200 (موفق)

  • Content-Type: application/json

  • Body: گزارش GirinofReport

  • محاسبات باید بر اساس اختلاف زمانی با ساعت استاندارد (۹:۰۰-۱۷:۰۰) انجام شود.

پاسخ خطا:

  • وضعیت: 500 با پیام Failed to generate report + پیام خطا
هندلر GetMonthlyReportHandler

توضیح:

این هندلر برای دریافت گزارش حضور و غیاب یک برنامه‌نویس در یک بازه زمانی مشخص است. ابتدا شناسه برنامه‌نویس، تاریخ شروع و پایان را بررسی کرده و در صورت معتبر بودن، گزارش را تولید و در قالب JSON بازمی‌گرداند. در صورت نامعتبر بودن ورودی‌ها یا بروز خطا، پیام خطای مناسب ارسال می‌شود.

  • متد: GET

  • اندپوینت یا مسیر: /report/monthly/{programmer_id}/{start_date}/{end_date}

  • ورودی:

    • programmer_id به عنوان پارامتر مسیر

    • start_date و end_date به عنوان پارامتر مسیر (فرمت: YYYY-MM-DD)

اعتبارسنجی:

  • اگر programmer_id ارسال‌ نشده باشد، باید Programmer ID is required را با وضعیت 400 برگرداند.
  • اگر start_date یا end_date ارسال‌ نشده باشد، باید Start date is required یا End date is required را با وضعیت 400 برگرداند.
  • اگر programmer_id عدد نباشد یا کمتر از ۱ باشد، باید Invalid programmer ID را با وضعیت 400 برگرداند.
  • اگر start_date یا end_date فرمت نامعتبر داشته باشند، باید Invalid start date format یا Invalid end date format را با وضعیت 400 برگرداند.
  • اگر end_date قبل از start_date باشد، باید End date cannot be before start date را با وضعیت 400 برگرداند.
  • اگر برنامه‌نویس با programmer_id داده‌شده وجود نداشته باشد، باید Programmer not found را با وضعیت 404 برگرداند.

پاسخ موفقیت‌آمیز:

  • وضعیت: 200 (موفق)

  • Content-Type: application/json

  • Body: گزارش MonthlySummary به شکل زیر:

{
  "programmer_id": "1",
  "total_days_present": 4,
  "total_overtime_minutes": 3,
  "total_delay_minutes": 2,
  "total_early_departure_minutes": 1
}
JSON
  • محاسبات باید بر اساس اختلاف زمانی با ساعت استاندارد (۹:۰۰-۱۷:۰۰) انجام شود.

پاسخ خطا:

  • وضعیت: 500 با پیام Failed to generate monthly report + پیام خطا
هندلر GetSalaryHandler

توضیح:

این هندلر برای محاسبه حقوق یک برنامه‌نویس در ۳۰ روز گذشته است. ابتدا شناسه برنامه‌نویس را بررسی کرده و در صورت معتبربودن، حقوق او را بر اساس سوابق حضور و غیاب محاسبه و در قالب JSON بازمی‌گرداند. در صورت عدم وجود اطلاعات یا بروز خطا، پیام خطای مناسب ارسال می‌شود.

  • متد: GET

  • اندپوینت یا مسیر: /salary/{programmer_id}

  • ورودی: programmer_id به عنوان پارامتر مسیر

اعتبارسنجی:

  • اگر programmer_id ارسال‌ نشده باشد، باید Programmer ID is required را با وضعیت 400 برگرداند.
  • اگر programmer_id عدد نباشد یا کمتر از ۱ باشد، باید Invalid programmer ID را با وضعیت 400 برگرداند.
  • اگر برنامه‌نویس با programmer_id داده‌شده وجود نداشته باشد، باید Programmer not found را با وضعیت 404 برگرداند.

پاسخ موفقیت‌آمیز:

  • وضعیت: 200 (موفق)

  • Content-Type: application/json

  • Body: گزارش SalaryReport به شکل زیر:

{
  "name": "Younes Sinwar",
  "total_days_present": 30,
  "total_overtime_minutes": 1,
  "total_delay_minutes": 2,
  "total_early_departure_minutes": 3,
  "total_salary": 3000.0
}
JSON
  • محاسبات باید بر اساس اختلاف زمانی با ساعت استاندارد (۹:۰۰-۱۷:۰۰) انجام شود.

محاسبه حقوق: حقوق بر اساس فرمول زیر محاسبه می‌شود:

حقوق پایه = تعداد روزهای حضور × DailyRate (۱۰۰)

اضافه‌کاری = مجموع دقیقه‌های اضافه‌کاری × OvertimeRate (۲۰)

جریمه تاخیر = مجموع دقیقه‌های تاخیر × DelayPenalty (۱۰)

حقوق نهایی = حقوق پایه + اضافه‌کاری - جریمه تاخیر
Plain text

پاسخ خطا:

  • وضعیت: 500 با پیام Failed to calculate salary + پیام خطا
هندلر CreateProgrammerHandler

توضیح:

این هندلر برای ایجاد یک برنامه‌نویس جدید در سیستم است. ابتدا داده‌های ورودی را بررسی کرده و در صورت معتبربودن، برنامه‌نویس را در پایگاه‌داده ثبت می‌کند. در صورت بروز خطا یا نامعتبربودن اطلاعات، پیام خطای مناسب ارسال می‌شود.

  • متد: POST

  • اندپوینت یا مسیر: /programmer

  • ورودی: بدنه JSON شامل:

{
  "name": "string"
}
JSON

اعتبارسنجی:

  • اگر name ارسال‌ نشده باشد، باید Invalid input format را با وضعیت 400 برگردانید.
    • اگر فیلد name خالی باشد، باید Name is required را با وضعیت 400 برگردانید.

پاسخ موفقیت‌آمیز:

  • وضعیت: 201 (ایجاد شده)

  • Content-Type: application/json

  • Body: شی programmer ایجادشده

پاسخ خطا:

  • وضعیت: 500 با پیام Failed to create programmer + پیام خطا
  • در تمام هندلرها اگر خطای داخلی سرور رخ دهد، باید وضعیت 500 برگردانده شود.

توابع Repository🔗

متدهای Repository زیر را پیاده‌سازی کنید:

func (repo *Repository) CreateAttendance(attendance *Attendance) error
func (repo *Repository) GetAttendancesByProgrammer(pid uint) ([]Attendance, error)
func (repo *Repository) GetAllAttendance() ([]Attendance, error)
func (repo *Repository) UpdateAttendance(attendance *Attendance) error
func (repo *Repository) DeleteAttendance(pid uint) error
func (repo *Repository) DeleteOneDayAttendance(pid uint, date time.Time) error
func (repo *Repository) GetGirinofReport(pid uint, date time.Time) (*GirinofReport, error)
func (repo *Repository) GetMonthlyReport(pid uint, startDate, endDate time.Time) (*MonthlySummary, error)
func (repo *Repository) CalculateSalary(pid uint, startDate, endDate time.Time) (*SalaryReport, error)
func (repo *Repository) CreateProgrammer(name string) (*Programmer, error)
func (repo *Repository) GetProgrammerByID(id uint) (*Programmer, error)
func (repo *Repository) IsExistDateAndProgrammerID(id uint, date time.Time) (*Attendance, bool)
Go
متد CreateAttendance
func (repo *Repository) CreateAttendance(attendance *Attendance) error
Go
  • رکورد Attendance جدیدی با تمام فیلدهای الزامی ایجاد می‌کند

  • در صورت وجود attendance تکراری (با همان ProgrammerID و Date) خطا برمی‌گرداند

  • آی‌دی تولید شده برای رکورد جدید باید غیرصفر باشد

  • در صورت خطا در عملیات پایگاه‌داده، خطا را برمی‌گرداند

متد GetAttendancesByProgrammer

‍‍‍

func (repo *Repository) GetAttendancesByProgrammer(pid uint) ([]Attendance, error)
Go
  • لیست تمام رکوردهای حضور و غیاب یک برنامه‌نویس را برمی‌گرداند

  • نتایج باید بر اساس تاریخ (date) به صورت نزولی مرتب شوند

  • حداکثر ۱۲ رکورد بازگردانده شود

  • در صورت عدم وجود رکورد، لیست خالی برمی‌گرداند

  • در صورت خطا در عملیات پایگاه‌داده، خطا را برمی‌گرداند

متد GetAllAttendance
func (repo *Repository) GetAllAttendance() ([]Attendance, error)
Go
  • لیست تمامی رکوردهای حضور و غیاب موجود در پایگاه‌داده را برمی‌گرداند.

  • در صورت عدم وجود رکورد، لیست خالی برمی‌گرداند.

  • در صورت خطا، خطا را برمی‌گرداند.

متد UpdateAttendance
func (repo *Repository) UpdateAttendance(attendance *Attendance) error
Go
  • رکورد موجود را با مقادیر جدید به‌روزرسانی می‌کند.

  • فیلدهای قابل به‌روزرسانی: date، check_in و check_out

  • در صورت عدم وجود رکورد مرتبط، خطا برمی‌گرداند.

  • در صورت خطا در عملیات پایگاه‌داده، خطا را برمی‌گرداند.

متد DeleteAttendance
func (repo *Repository) DeleteAttendance(pid uint) error
Go
  • تمام رکوردهای حضور و غیاب مربوط به یک برنامه‌نویس را حذف می‌کند.

  • پس از حذف، هیچ رکوردی با ProgrammerID مربوطه نباید وجود داشته باشد.

  • در صورت خطا در عملیات پایگاه‌داده، خطا را برمی‌گرداند.

متد DeleteOneDayAttendance
func (repo *Repository) DeleteOneDayAttendance(pid uint, date time.Time) error
Go
  • رکورد حضور و غیاب مربوط به یک روز خاص را حذف می‌کند.

  • تاریخ باید دقیقاً مطابقت داشته باشد (برای انجام این کار تمامی رکوردهای تاریخ آن روز را از ساعت 00:00:00 تا ساعت 24:00:00 حذف کنید. می‌توانید از date.Truncate استفاده کنید)

  • در صورت عدم وجود رکورد، خطا برمی‌گرداند.

  • در صورت خطا در عملیات پایگاه‌داده، خطا را برمی‌گرداند.

متد GetGirinofReport
func (repo *Repository) GetGirinofReport(pid uint, date time.Time) (*GirinofReport, error)
Go
  • گزارش روزانه شامل مجموع تاخیرها (total_delay_minutes) و خروج‌های زودهنگام (total_early_departure_minutes) را همراه با programmer_id ایجاد می‌کند.

  • محاسبات باید بر اساس اختلاف زمانی با ساعت استاندارد (۹:۰۰-۱۷:۰۰) انجام شود.

  • در صورت عدم وجود رکورد در تاریخ مشخص، خطا برمی‌گرداند.

  • در صورت خطا در محاسبات یا عملیات پایگاه‌داده، خطا را برمی‌گرداند.

متد GetMonthlyReport
func (repo *Repository) GetMonthlyReport(id string, startDate, endDate time.Time) (*MonthlySummary, error)
Go
  • گزارش ماهانه شامل آمار حضور، اضافه‌کاری و تاخیرها را در بازه زمانی داده شده ایجاد می‌کند

  • محاسبات باید بر اساس اختلاف زمانی با ساعت استاندارد (۹:۰۰-۱۷:۰۰) انجام شود.

  • این گزارش شامل آی‌دی برنامه‌نویس (programmer_id) تعداد روزهای حضور (total_days_present)، مجموع دقیقه‌های اضافه‌کاری (total_overtime_minutes)، مجموع دقیقه‌های تاخیر (total_delay_minutes) و مجموع دقیقه‌های خروج زودهنگام (total_early_departure_minutes) است.

  • در صورت خطا در محاسبات یا عملیات پایگاه‌داده، خطا را برمی‌گرداند.

متد CalculateSalary
func (repo *Repository) CalculateSalary(id string, startDate, endDate time.Time) (*SalaryReport, error)
Go
  • حقوق (SalaryReport) یک برنامه‌نویس خاص را در سی روز گذشته محاسبه می‌کند.

  • محاسبات باید بر اساس اختلاف زمانی با ساعت استاندارد (۹:۰۰-۱۷:۰۰) انجام شود.

  • حقوق خالص را بر اساس فرمول زیر محاسبه می‌کند:

    • حقوق پایه = تعداد روزهای حضور × نرخ روزانه (DailyRate)

    • اضافه‌کاری = مجموع دقیقه‌های اضافه‌کاری × نرخ اضافه‌کاری (OvertimeRate)

    • جریمه تاخیر = مجموع دقیقه‌های تاخیر × نرخ جریمه (DelayPenalty)

    • حقوق نهایی = حقوق پایه + اضافه‌کاری - جریمه تاخیر

  • این گزارش شامل نام برنامه‌نویس (name)، تعداد روزهای حضور (total_days_present)، مجموع دقیقه‌های اضافه‌کاری (total_overtime_minutes)، مجموع دقیقه‌های تاخیر (total_delay_minutes)، مجموع دقیقه‌های خروج زودهنگام (total_early_departure_minutes) و مجموع حقوق (total_salary) است.

  • در صورت خطا در محاسبات یا عملیات پایگاه‌داده، خطا را برمی‌گرداند

متد CreateProgrammer
func (repo *Repository) CreateProgrammer(name string) (*Programmer, error)
Go
  • یک برنامه‌نویس جدید ایجاد می‌کند.
  • آی‌دی تولید شده باید غیرصفر باشد.
  • در صورت خطا در عملیات پایگاه‌داده، خطا را برمی‌گرداند.
متد GetProgrammerByID
func (repo *Repository) GetProgrammerByID(id uint) (*Programmer, error)
Go
  • اطلاعات کامل برنامه‌نویس را بر اساس ID برمی‌گرداند.

  • در صورت عدم وجود برنامه‌نویس با ID داده شده، خطا برمی‌گرداند.

  • در صورت خطا در عملیات پایگاه‌داده، خطا را برمی‌گرداند.

متد IsExistDateAndProgrammerID
func (repo *Repository) IsExistDateAndProgrammerID(id uint, date time.Time) (*Attendance, bool)
Go
  • بررسی می‌کند که آیا رکورد حضور و غیاب برای یک برنامه‌نویس خاص در تاریخ مشخص وجود دارد یا خیر.

  • تاریخ باید دقیقاً مطابقت داشته باشد (برای انجام این کار تمامی رکوردهای تاریخ آن در آن روز یعنی از ساعت 00:00:00 تا ساعت 24:00:00 را بررسی کنید)

  • در صورت وجود، رکورد کامل را همراه با true برمی‌گرداند.

  • در صورت عدم وجود، nil و false برمی‌گرداند.

  • تاریخ باید دقیقاً مطابقت داشته باشد (با در نظر گرفتن ساعت ۰۰:۰۰:۰۰)

نکات

  • شما باید در این سوال از GORM و ویژگی‌های آن استفاده کنید و به جز پکیج‌های موجود در پروژه اولیه اجازه استفاده از پکیج دیگری را ندارید.

  • فایل db.go برای اتصال GORM به پایگاه‌داده با استفاده از الگوی طراحی singleton است. شما نیازی به تغییر آن ندارید به جز تغییر ثابت‌ها برای تست روی محیط لوکال خود.

  • فایل‌ها و کدهای پروژه‌اولیه را نباید تغییر بدهید و صرفا باید مکان‌هایی که با کامنت // TODO Implement برای شما مشخص شده را پیاده‌سازی کنید.

  • نیازی به ارسال فایل‌های go.mod و go.sum ندارید!

چه چیزی را آپلود کنید

پس از پیاده‌سازی ویژگی‌های خواسته‌شده،‌ فقط دایرکتوری app که شامل سه فایل handlers.go و models.go و repository.go است را فشرده کرده و صورت زیپ ارسال کنید. فایل‌هایی که نیاز به ویرایش دارند:

  • app/models.go‍
  • app/handlers.go
  • app/repository.go