Skip to content

Commit b211974

Browse files
authored
Merge pull request #73 from PyAr/automatic_wizards_agenda
Wizards agenda
2 parents 072b1ff + 80d77c3 commit b211974

File tree

14 files changed

+850
-58
lines changed

14 files changed

+850
-58
lines changed

.github/workflows/pythonapp.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ on:
99
pull_request:
1010
branches: [ master ]
1111

12+
env:
13+
TOKEN: foo
14+
1215
jobs:
1316
build:
1417

README.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ pip install -e .
3030

3131
y estas listo para trabajar.
3232

33+
`pip install freezegun` para correr los tests.
34+
35+
### Python 3.12
36+
37+
`pip install setuptools`
38+
3339
## Testeo
3440

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

5965
* `/su <password>` para reclamar permisos de admin, reemplazando `<password>` por la contraseña que hayamos
6066
elegido en la envvar `PYCAMP_BOT_MASTER_KEY`
61-
* `/agregar_pycamp <pycamp_name>` para crear un pycamp en la deb
67+
* `/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.
6268
* `/activar_pycamp <pycamp_name>` activa un pycamp
63-
* `/empezar_pycamp` setea la fecha de inicio del pycamp activo
6469
* `/empezar_carga_proyectos` habilita la carga de los proyectos. En este punto los pycampistas pueden cargar sus proyectos,
6570
enviandole al bot el comando `/cargar_proyecto`
6671
* `/terminar_carga_proyectos` termina carga proyectos
@@ -72,8 +77,22 @@ Para generar el schedule:
7277
* `/cronogramear` te va a preguntar cuantos dias queres cronogramear y cuantos slots por dia tenes y hacer el cronograma.
7378
* `/cambiar_slot` toma un nombre de proyecto y un slot; y te cambia ese proyecto a ese slot.
7479

80+
Para agendar los magos:
81+
82+
1. Todos los candidatos tienen que haberse registrado con `/ser_magx`
83+
2. Tiene que estar creado el schedule de presentaciones de proyectos (`/cronogramear`)
84+
85+
* `/agendar_magx` Asigna un mago por hora durante todo el PyCamp.
86+
* De 9 a 13 y de 14 a 19.
87+
* El primer día arranca después del almuerzo (14hs).
88+
* El último día termina al almuerzo (13hs).
89+
7590
### Flujo pycampista
7691

7792
* `/cargar_proyecto` carga un proyecto (si está habilitada la carga)
7893
* `/votar` envia opciones para votar (si está habilitada la votacion)
7994
* `/ver_cronograma` te muestra el cronograma!
95+
* `/ser_magx` te registra como mago.
96+
* `/ver_magx` Lista los magos registrados.
97+
* `/evocar_magx` llama al mago de turno para pedirle ayuda.
98+
* `/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.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# https://docs.peewee-orm.com/en/latest/peewee/playhouse.html#schema-migrations
2+
3+
from datetime import datetime, timedelta
4+
from playhouse.migrate import *
5+
import peewee as pw
6+
7+
from pycamp_bot.models import Pycampista, Slot, Pycamp
8+
9+
10+
my_db = pw.SqliteDatabase('pycamp_projects.db')
11+
migrator = SqliteMigrator(my_db)
12+
13+
from pycamp_bot.models import Pycamp
14+
15+
16+
migrate(
17+
migrator.add_column( # wizard_slot_duration = pw.IntegerField(default=60, null=False)
18+
Pycamp._meta.table_name,
19+
'wizard_slot_duration',
20+
Pycamp.wizard_slot_duration
21+
),
22+
migrator.add_column( # current_wizard = pw.ForeignKeyField(Pycampista)
23+
Slot._meta.table_name,
24+
'current_wizard_id',
25+
Slot.current_wizard
26+
),
27+
)
28+
29+
p = Pycamp.get()
30+
p.end = datetime(2024,6,23,23,59,59,99)
31+
p.end = datetime(2024,6,23,23,59,59,999999)
32+
p.save()
33+

setup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
'munch==2.5.0',
1616
'python-telegram-bot==20.2',
1717
'peewee==3.16.0',
18+
'pytest==8.2.2',
19+
'freezegun==1.5.1',
1820
],
1921
test_suite='tests'
2022
)

src/pycamp_bot/commands/auth.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ def is_admin(update, context):
2828

2929
def admin_needed(f):
3030
async def wrap(*args, **kargs):
31-
logger.info('Admin nedeed wrapper')
3231
update, context = args
3332
if is_admin(*args):
3433
return await f(*args)

src/pycamp_bot/commands/manage_pycamp.py

Lines changed: 89 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
import datetime
2-
from telegram.ext import CommandHandler
2+
from telegram.ext import ConversationHandler, CommandHandler, MessageHandler, filters
33
from pycamp_bot.models import Pycamp
44
from pycamp_bot.models import Pycampista
55
from pycamp_bot.models import PycampistaAtPycamp
66
from pycamp_bot.commands.auth import admin_needed
77
from pycamp_bot.logger import logger
88

99

10+
SET_DATE_STATE = "set_fate"
11+
SET_DURATION_STATE = "set_duration"
12+
WRAP_UP_STATE = "wrap_up"
13+
14+
1015
def get_pycamp_by_name(name):
1116
pycamps = Pycamp.select().where(Pycamp.headquarters == name)
1217
count = pycamps.count()
@@ -68,36 +73,101 @@ async def set_active_pycamp(update, context):
6873

6974
@admin_needed
7075
async def add_pycamp(update, context):
71-
parameters = update.message.text.split(' ')
72-
if not len(parameters) == 2:
76+
parameters = update.message.text.split(' ', 1)
77+
if len(parameters) < 2:
7378
await context.bot.send_message(
7479
chat_id=update.message.chat_id,
75-
text="El comando necesita un parametro (pycamp name)")
80+
text="El comando necesita un parametro (headquarters)")
81+
return
82+
hq = parameters[1].strip()
83+
if not hq:
84+
await context.bot.send_message(
85+
chat_id=update.message.chat_id,
86+
text="El parámetro headquarters no puede ser vacío")
7687
return
7788

78-
pycamp = Pycamp.get_or_create(headquarters=parameters[1])[0]
89+
pycamp = Pycamp.get_or_create(headquarters=hq, active=True)[0]
90+
pycamp.set_as_only_active()
91+
logger.info('Creado: {}'.format(pycamp))
7992

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

84100

85-
@active_needed
86-
@admin_needed
87-
async def start_pycamp(update, context):
88-
parameters = update.message.text.split(' ')
89-
if len(parameters) == 2:
90-
date = datetime.datetime.fromisoformat(parameters[1])
91-
else:
92-
date = datetime.datetime.now()
101+
async def define_start_date(update, context):
102+
text = update.message.text
103+
try:
104+
start_date = datetime.datetime.fromisoformat(text)
105+
except ValueError:
106+
await context.bot.send_message(
107+
chat_id=update.message.chat_id,
108+
text="mmm no entiendo esa fecha\. El formato esperado es `yyyy-mm-dd`\. ¿De nuevo?",
109+
parse_mode="MarkdownV2"
110+
)
111+
return SET_DATE_STATE
112+
113+
_, pycamp = get_active_pycamp()
114+
pycamp.init = start_date
115+
pycamp.save()
93116

94-
is_active, pycamp = get_active_pycamp()
95-
pycamp.init = date
117+
await context.bot.send_message(
118+
chat_id=update.message.chat_id,
119+
text="¿Cuantos días dura el PyCamp?"
120+
)
121+
return SET_DURATION_STATE
122+
123+
124+
async def define_duration(update, context):
125+
text = update.message.text.strip()
126+
try:
127+
duration = int(text)
128+
except ValueError:
129+
await context.bot.send_message(
130+
chat_id=update.message.chat_id,
131+
text="mmm no entiendo. Poné un número entero porfa."
132+
)
133+
return SET_DURATION_STATE
134+
135+
_, pycamp = get_active_pycamp()
136+
pycamp.end = pycamp.init + datetime.timedelta(
137+
days=duration - 1,
138+
hours=23,
139+
minutes=59,
140+
seconds=59,
141+
milliseconds=99
142+
)
96143
pycamp.save()
97144

145+
msg = "Listo, el PyCamp '{}' está activo, desde el {} hasta el {}".format(
146+
pycamp.headquarters,
147+
pycamp.init.date(),
148+
pycamp.end.date()
149+
)
150+
await context.bot.send_message(
151+
chat_id=update.message.chat_id,
152+
text=msg
153+
)
154+
155+
156+
async def cancel(update, context):
98157
await context.bot.send_message(
99158
chat_id=update.message.chat_id,
100-
text="Empezó Pycamp :) ! {}".format(date))
159+
text="Se canceló la carga del PyCamp...")
160+
return ConversationHandler.END
161+
162+
163+
load_start_pycamp = ConversationHandler(
164+
entry_points=[CommandHandler('empezar_pycamp', add_pycamp)],
165+
states={
166+
SET_DATE_STATE: [MessageHandler(filters.TEXT, define_start_date)],
167+
SET_DURATION_STATE: [MessageHandler(filters.TEXT, define_duration)]
168+
},
169+
fallbacks=[CommandHandler('cancel', cancel)]
170+
)
101171

102172

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

112182
is_active, pycamp = get_active_pycamp()
183+
pycamp.active = False
113184
pycamp.end = date
114185
pycamp.save()
115186

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

163234

164235
def set_handlers(application):
165-
application.add_handler(
166-
CommandHandler('empezar_pycamp', start_pycamp))
236+
application.add_handler(load_start_pycamp)
167237
application.add_handler(
168238
CommandHandler('terminar_pycamp', end_pycamp))
169239
application.add_handler(
170240
CommandHandler('activar_pycamp', set_active_pycamp))
171-
application.add_handler(
172-
CommandHandler('agregar_pycamp', add_pycamp))
173241
application.add_handler(
174242
CommandHandler('pycamps', list_pycamps))
175243
application.add_handler(
176244
CommandHandler('voy_al_pycamp', add_pycampista_to_pycamp))
177245
application.add_handler(
178246
CommandHandler('pycampistas', list_pycampistas))
179-
180-

0 commit comments

Comments
 (0)