Skip to content

Commit

Permalink
Merge pull request #73 from PyAr/automatic_wizards_agenda
Browse files Browse the repository at this point in the history
Wizards agenda
  • Loading branch information
WinnaZ authored Jun 22, 2024
2 parents 072b1ff + 80d77c3 commit b211974
Show file tree
Hide file tree
Showing 14 changed files with 850 additions and 58 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/pythonapp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ on:
pull_request:
branches: [ master ]

env:
TOKEN: foo

jobs:
build:

Expand Down
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ pip install -e .

y estas listo para trabajar.

`pip install freezegun` para correr los tests.

### Python 3.12

`pip install setuptools`

## Testeo

Para correr el bot ejecutá (desde el virtualenv):
Expand Down Expand Up @@ -58,9 +64,8 @@ En este momento ya se puede hablar con el bot. ¿Qué le digo?

* `/su <password>` para reclamar permisos de admin, reemplazando `<password>` por la contraseña que hayamos
elegido en la envvar `PYCAMP_BOT_MASTER_KEY`
* `/agregar_pycamp <pycamp_name>` para crear un pycamp en la deb
* `/empezar_pycamp <pycamp_name>` inicia el flujo de creación de un pycamp. Lo carga en la db, pide fecha de inicio y duración. Lo deja activo.
* `/activar_pycamp <pycamp_name>` activa un pycamp
* `/empezar_pycamp` setea la fecha de inicio del pycamp activo
* `/empezar_carga_proyectos` habilita la carga de los proyectos. En este punto los pycampistas pueden cargar sus proyectos,
enviandole al bot el comando `/cargar_proyecto`
* `/terminar_carga_proyectos` termina carga proyectos
Expand All @@ -72,8 +77,22 @@ Para generar el schedule:
* `/cronogramear` te va a preguntar cuantos dias queres cronogramear y cuantos slots por dia tenes y hacer el cronograma.
* `/cambiar_slot` toma un nombre de proyecto y un slot; y te cambia ese proyecto a ese slot.

Para agendar los magos:

1. Todos los candidatos tienen que haberse registrado con `/ser_magx`
2. Tiene que estar creado el schedule de presentaciones de proyectos (`/cronogramear`)

* `/agendar_magx` Asigna un mago por hora durante todo el PyCamp.
* De 9 a 13 y de 14 a 19.
* El primer día arranca después del almuerzo (14hs).
* El último día termina al almuerzo (13hs).

### Flujo pycampista

* `/cargar_proyecto` carga un proyecto (si está habilitada la carga)
* `/votar` envia opciones para votar (si está habilitada la votacion)
* `/ver_cronograma` te muestra el cronograma!
* `/ser_magx` te registra como mago.
* `/ver_magx` Lista los magos registrados.
* `/evocar_magx` llama al mago de turno para pedirle ayuda.
* `/ver_agenda_magx completa` te muestra la agenda de magos del PyCamp. El parámetro `completa` es opcional, si se omite solo muestra los turnos pendientes.
33 changes: 33 additions & 0 deletions migrations/migrate_to_wizards_scheduling.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# https://docs.peewee-orm.com/en/latest/peewee/playhouse.html#schema-migrations

from datetime import datetime, timedelta
from playhouse.migrate import *
import peewee as pw

from pycamp_bot.models import Pycampista, Slot, Pycamp


my_db = pw.SqliteDatabase('pycamp_projects.db')
migrator = SqliteMigrator(my_db)

from pycamp_bot.models import Pycamp


migrate(
migrator.add_column( # wizard_slot_duration = pw.IntegerField(default=60, null=False)
Pycamp._meta.table_name,
'wizard_slot_duration',
Pycamp.wizard_slot_duration
),
migrator.add_column( # current_wizard = pw.ForeignKeyField(Pycampista)
Slot._meta.table_name,
'current_wizard_id',
Slot.current_wizard
),
)

p = Pycamp.get()
p.end = datetime(2024,6,23,23,59,59,99)
p.end = datetime(2024,6,23,23,59,59,999999)
p.save()

2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
'munch==2.5.0',
'python-telegram-bot==20.2',
'peewee==3.16.0',
'pytest==8.2.2',
'freezegun==1.5.1',
],
test_suite='tests'
)
1 change: 0 additions & 1 deletion src/pycamp_bot/commands/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ def is_admin(update, context):

def admin_needed(f):
async def wrap(*args, **kargs):
logger.info('Admin nedeed wrapper')
update, context = args
if is_admin(*args):
return await f(*args)
Expand Down
112 changes: 89 additions & 23 deletions src/pycamp_bot/commands/manage_pycamp.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import datetime
from telegram.ext import CommandHandler
from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, filters
from pycamp_bot.models import Pycamp
from pycamp_bot.models import Pycampista
from pycamp_bot.models import PycampistaAtPycamp
from pycamp_bot.commands.auth import admin_needed
from pycamp_bot.logger import logger


SET_DATE_STATE = "set_fate"
SET_DURATION_STATE = "set_duration"
WRAP_UP_STATE = "wrap_up"


def get_pycamp_by_name(name):
pycamps = Pycamp.select().where(Pycamp.headquarters == name)
count = pycamps.count()
Expand Down Expand Up @@ -68,36 +73,101 @@ async def set_active_pycamp(update, context):

@admin_needed
async def add_pycamp(update, context):
parameters = update.message.text.split(' ')
if not len(parameters) == 2:
parameters = update.message.text.split(' ', 1)
if len(parameters) < 2:
await context.bot.send_message(
chat_id=update.message.chat_id,
text="El comando necesita un parametro (pycamp name)")
text="El comando necesita un parametro (headquarters)")
return
hq = parameters[1].strip()
if not hq:
await context.bot.send_message(
chat_id=update.message.chat_id,
text="El parámetro headquarters no puede ser vacío")
return

pycamp = Pycamp.get_or_create(headquarters=parameters[1])[0]
pycamp = Pycamp.get_or_create(headquarters=hq, active=True)[0]
pycamp.set_as_only_active()
logger.info('Creado: {}'.format(pycamp))

msg = "El Pycamp {} fue creado.\n¿Cuándo empieza? (formato yyyy-mm-dd)"
await context.bot.send_message(
chat_id=update.message.chat_id,
text="El Pycamp {} fue creado.".format(pycamp.headquarters))
text=msg.format(pycamp.headquarters)
)
return SET_DATE_STATE


@active_needed
@admin_needed
async def start_pycamp(update, context):
parameters = update.message.text.split(' ')
if len(parameters) == 2:
date = datetime.datetime.fromisoformat(parameters[1])
else:
date = datetime.datetime.now()
async def define_start_date(update, context):
text = update.message.text
try:
start_date = datetime.datetime.fromisoformat(text)
except ValueError:
await context.bot.send_message(
chat_id=update.message.chat_id,
text="mmm no entiendo esa fecha\. El formato esperado es `yyyy-mm-dd`\. ¿De nuevo?",
parse_mode="MarkdownV2"
)
return SET_DATE_STATE

_, pycamp = get_active_pycamp()
pycamp.init = start_date
pycamp.save()

is_active, pycamp = get_active_pycamp()
pycamp.init = date
await context.bot.send_message(
chat_id=update.message.chat_id,
text="¿Cuantos días dura el PyCamp?"
)
return SET_DURATION_STATE


async def define_duration(update, context):
text = update.message.text.strip()
try:
duration = int(text)
except ValueError:
await context.bot.send_message(
chat_id=update.message.chat_id,
text="mmm no entiendo. Poné un número entero porfa."
)
return SET_DURATION_STATE

_, pycamp = get_active_pycamp()
pycamp.end = pycamp.init + datetime.timedelta(
days=duration - 1,
hours=23,
minutes=59,
seconds=59,
milliseconds=99
)
pycamp.save()

msg = "Listo, el PyCamp '{}' está activo, desde el {} hasta el {}".format(
pycamp.headquarters,
pycamp.init.date(),
pycamp.end.date()
)
await context.bot.send_message(
chat_id=update.message.chat_id,
text=msg
)


async def cancel(update, context):
await context.bot.send_message(
chat_id=update.message.chat_id,
text="Empezó Pycamp :) ! {}".format(date))
text="Se canceló la carga del PyCamp...")
return ConversationHandler.END


load_start_pycamp = ConversationHandler(
entry_points=[CommandHandler('empezar_pycamp', add_pycamp)],
states={
SET_DATE_STATE: [MessageHandler(filters.TEXT, define_start_date)],
SET_DURATION_STATE: [MessageHandler(filters.TEXT, define_duration)]
},
fallbacks=[CommandHandler('cancel', cancel)]
)


@active_needed
Expand All @@ -110,6 +180,7 @@ async def end_pycamp(update, context):
date = datetime.datetime.now()

is_active, pycamp = get_active_pycamp()
pycamp.active = False
pycamp.end = date
pycamp.save()

Expand Down Expand Up @@ -162,19 +233,14 @@ async def list_pycampistas(update, context):


def set_handlers(application):
application.add_handler(
CommandHandler('empezar_pycamp', start_pycamp))
application.add_handler(load_start_pycamp)
application.add_handler(
CommandHandler('terminar_pycamp', end_pycamp))
application.add_handler(
CommandHandler('activar_pycamp', set_active_pycamp))
application.add_handler(
CommandHandler('agregar_pycamp', add_pycamp))
application.add_handler(
CommandHandler('pycamps', list_pycamps))
application.add_handler(
CommandHandler('voy_al_pycamp', add_pycampista_to_pycamp))
application.add_handler(
CommandHandler('pycampistas', list_pycampistas))


Loading

0 comments on commit b211974

Please sign in to comment.