Skip to content
2 changes: 1 addition & 1 deletion src/pycamp_bot/commands/announcements.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ async def announce(update: Update, context: CallbackContext) -> str:
chat_id=update.message.chat_id,
text=ERROR_MESSAGES["no_admin"],
)
logger.warn(f"Pycampista {state.username} no contiene proyectos creados.")
logger.warning(f"Pycampista {state.username} no contiene proyectos creados.")
return ConversationHandler.END
else:
state.projects = Project.select()
Expand Down
30 changes: 21 additions & 9 deletions src/pycamp_bot/commands/projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import textwrap

import peewee
from peewee import JOIN
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, LinkPreviewOptions
from telegram.ext import CallbackQueryHandler, CommandHandler, ConversationHandler, MessageHandler, filters
from pycamp_bot.models import Pycampista, Project, Slot, Vote
Expand Down Expand Up @@ -528,9 +529,9 @@ async def show_my_projects(update, context):
)
votes = (
Vote
.select(Project, Slot)
.select(Vote, Project, Slot)
.join(Project)
.join(Slot)
.join(Slot, join_type=JOIN.LEFT_OUTER)
.where(
(Vote.pycampista == user) &
Vote.interest
Expand All @@ -544,17 +545,28 @@ async def show_my_projects(update, context):
prev_slot_day_code = None

for vote in votes:
slot_day_code = vote.project.slot.code[0]
slot_day_name = get_slot_weekday_name(slot_day_code)
slot = vote.project.slot
if slot is None:
slot_day_code = None
slot_day_name = "Sin asignar"
else:
slot_day_code = slot.code[0]
slot_day_name = get_slot_weekday_name(slot_day_code)

if slot_day_code != prev_slot_day_code:
text_chunks.append(f'*{slot_day_name}*')

project_lines = [
f'{vote.project.slot.start}:00',
escape_markdown(vote.project.name),
f'Owner: @{escape_markdown(vote.project.owner.username)}',
]
if slot is None:
project_lines = [
escape_markdown(vote.project.name),
f'Owner: @{escape_markdown(vote.project.owner.username)}',
]
else:
project_lines = [
f'{slot.start}:00',
escape_markdown(vote.project.name),
f'Owner: @{escape_markdown(vote.project.owner.username)}',
]

text_chunks.append('\n'.join(project_lines))

Expand Down
2 changes: 1 addition & 1 deletion src/pycamp_bot/commands/schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ async def create_slot(update, context):
chat_id=update.message.chat_id,
text="Genial! Slots Asignados"
)
make_schedule(update, context)
await make_schedule(update, context)
return ConversationHandler.END


Expand Down
12 changes: 9 additions & 3 deletions src/pycamp_bot/commands/voting.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ async def start_voting(update, context):
async def button(update, context):
'''Save user vote in the database'''
query = update.callback_query
username = query.message['chat']['username']
username = query.from_user.username
chat_id = query.message.chat_id
user = Pycampista.get_or_create(username=username, chat_id=chat_id)[0]
project_name = query.message['text']
project_name = query.message.text

# Get project from the database
project = Project.get(Project.name == project_name)
Expand Down Expand Up @@ -95,7 +95,11 @@ async def vote(update, context):

# if there is not project in the database, create a new project
if not Project.select().exists():
Project.create(name='PROYECTO DE PRUEBA')
user = Pycampista.get_or_create(
username=update.message.from_user.username,
chat_id=str(update.message.chat_id),
)[0]
Project.create(name='PROYECTO DE PRUEBA', owner=user)

# ask user for each project in the database
for project in Project.select():
Expand All @@ -121,6 +125,8 @@ async def end_voting(update, context):
await update.message.reply_text("Selección cerrada")
await msg_to_active_pycamp_chat(context.bot, "La selección de proyectos ha finalizado.")


@admin_needed
async def vote_count(update, context):
votes = [vote.pycampista_id for vote in Vote.select()]
vote_count = len(set(votes))
Expand Down
74 changes: 49 additions & 25 deletions src/pycamp_bot/commands/wizard.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import random
from collections import defaultdict
from datetime import datetime, timedelta
from itertools import cycle
from telegram.ext import CommandHandler
import random
from zoneinfo import ZoneInfo

from telegram.error import BadRequest
from pycamp_bot.models import Pycampista, WizardAtPycamp
from telegram.ext import CommandHandler

from pycamp_bot.commands.auth import admin_needed
from pycamp_bot.commands.manage_pycamp import get_active_pycamp
from pycamp_bot.logger import logger
from pycamp_bot.models import Pycampista, WizardAtPycamp
from pycamp_bot.utils import escape_markdown, active_pycamp_needed


Expand Down Expand Up @@ -240,7 +243,7 @@ async def schedule_wizards(update, context, pycamp=None):
parse_mode="MarkdownV2"
)
except BadRequest as e:
m = "Coulnd't return the Wizards list to the admin. ".format(update.message.from_user.username)
m = "Couldn't return the Wizards list to the admin ({}).".format(update.message.from_user.username)
if len(msg) >= MSG_MAX_LEN:
m += "The message is too long. Check the data in the DB ;-)"
logger.exception(m)
Expand All @@ -264,44 +267,65 @@ def format_wizards_schedule(agenda):
)
return msg

def aux_resolve_show_all(message):
show_all = False
parameters = message.text.strip().split(' ', 1)
if len(parameters) == 2:
flag = parameters[1].strip().lower()
show_all = (flag == "completa") # Once here, the only parameter must be valid
if not show_all:
# The parameter was something else...
def aux_resolve_show_all(context):
"""Usa context.args: sin args o 'completa' = agenda completa; 'futuros' = solo turnos futuros."""
show_all = True # por defecto mostrar toda la agenda (evita problemas de timezone en containers)
args = (context.args or [])
if len(args) == 1:
flag = args[0].strip().lower()
if flag == "completa":
show_all = True
elif flag == "futuros":
show_all = False
else:
raise ValueError("Wrong parameter")
elif len(parameters) > 2:
# Too many parameters...
raise ValueError("Wrong parameter")
elif len(args) > 1:
raise ValueError("Too many parameters")
return show_all


@active_pycamp_needed
async def show_wizards_schedule(update, context, pycamp=None):
try:
show_all = aux_resolve_show_all(update.message)
show_all = aux_resolve_show_all(context)
except ValueError:
await context.bot.send_message(
chat_id=update.message.chat_id,
text="El comando solo acepta un parámetro (opcional): 'completa'. ¿Probás de nuevo?",
text="El comando acepta un parámetro opcional: 'completa' (ver todo) o 'futuros' (solo turnos por venir). ¿Probás de nuevo?",
)
return

agenda = WizardAtPycamp.select().where(WizardAtPycamp.pycamp == pycamp)
if not show_all:
agenda = agenda.where(WizardAtPycamp.end > datetime.now())
# Solo futuros: comparar con hora Argentina (los slots en DB son hora local Córdoba)
now_argentina = datetime.now(ZoneInfo("America/Argentina/Cordoba")).replace(tzinfo=None)
agenda = agenda.where(WizardAtPycamp.end > now_argentina)
agenda = agenda.order_by(WizardAtPycamp.init)

msg = format_wizards_schedule(agenda)

await context.bot.send_message(
chat_id=update.message.chat_id,
text=msg,
parse_mode="MarkdownV2"
)
count = agenda.count()
if count == 0:
if show_all:
msg = (
"Agenda de magxs:\n\n"
"No hay turnos cargados. Un admin debe ejecutar /agendar_magx "
"después de que magxs se anoten con /ser_magx."
)
else:
msg = (
"Agenda de magxs:\n\n"
"No hay turnos futuros. Probá con /ver_agenda_magx completa para ver toda la agenda."
)
await context.bot.send_message(
chat_id=update.message.chat_id,
text=msg,
)
else:
msg = format_wizards_schedule(agenda)
await context.bot.send_message(
chat_id=update.message.chat_id,
text=msg,
parse_mode="MarkdownV2",
)
logger.debug("Wizards schedule delivered to {}".format(update.message.from_user.username))


Expand Down
5 changes: 3 additions & 2 deletions src/pycamp_bot/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,11 @@ def __str__(self):
return rv_str

def set_as_only_active(self):
active = Pycamp.select().where(Pycamp.active)
active = list(Pycamp.select().where(Pycamp.active))
for p in active:
p.active = False
Pycamp.bulk_update(active, fields=[Pycamp.active])
if active:
Pycamp.bulk_update(active, fields=[Pycamp.active])
self.active = True
self.save()

Expand Down
5 changes: 4 additions & 1 deletion src/pycamp_bot/scheduler/schedule_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ def value(self, state):
# were at the begining
vote_quantity = sum([len(self.data.projects[project].votes)
for project in slot_projects])
most_voted_cost += (slot_number * vote_quantity) / self.total_participants
denom = max(1, self.total_participants)
most_voted_cost += (slot_number * vote_quantity) / denom

for project, slot in state:
project_data = self.data.projects[project]
Expand Down Expand Up @@ -221,6 +222,8 @@ def hill_climbing(problem, initial_state):

while True:
neighboors = [(n, problem.value(n)) for n in problem.neighboors(current_state)]
if not neighboors:
return current_state
best_neighbour, best_value = max(neighboors, key=itemgetter(1))

if best_value > current_value:
Expand Down