package main import ( "fmt" "time" ) // FilaEvento es un evento ya resuelto para pintarlo en una celda del mes. type FilaEvento struct { Hora string Texto string Quien string Estado Estado Externas bool // la pidió la asamblea de externas Etiqueta string Sigue bool // dia intermedio o final de un evento de varios dias } type Celda struct { Dia Dia Numero int Vacia bool Hoy bool Pasado bool Libre bool // se puede pedir: dia reservable, dentro de la ventana y sin ocupar Turno Turno EtiquetaTurno string // solo en el viernes, para no repetirla tres veces Eventos []FilaEvento } type Semana struct { Dias []Celda } type Mes struct { Nombre string Anio int Semanas []Semana } // Vista arma los meses que se pintan, a partir del estado del registro. // Aqui no hay horarios semanales: esos siguen en calendario.html, que se dibuja // con su propio guion y no se toca. type Vista struct { roles map[string]Rol reglas Reglas exc map[Dia]Turno eventos []Evento nombres map[string]string hoy Dia } func (v Vista) eventosDe(d Dia) []FilaEvento { var out []FilaEvento for _, e := range v.eventos { if !e.Estado.Ocupa() || d < e.Desde || d > e.Hasta { continue } quien := v.nombres[e.Solicitante] if quien == "" { quien = e.Solicitante } externas := v.roles[e.Solicitante] == RolExternas switch { case externas: quien = e.Amadrinado + " · asamblea de externas" case e.Externo(): quien = fmt.Sprintf("%s · amadrina %s", e.Amadrinado, quien) } out = append(out, FilaEvento{ Hora: e.Hora, Texto: e.Titulo, Quien: quien, Estado: e.Estado, Externas: externas, Etiqueta: etiquetaEstado(e.Estado, externas), Sigue: d != e.Desde, }) } return out } func (v Vista) celda(d Dia, numero int) Celda { turno := v.reglas.TurnoDe(d, v.exc) etiqueta := "" if turno != "" && d.DiaISO() == 5 { etiqueta = turno.Etiqueta() } primero, ultimo := v.reglas.Ventana(v.hoy) eventos := v.eventosDe(d) return Celda{ Dia: d, Numero: numero, Hoy: d == v.hoy, Pasado: d < v.hoy, Libre: len(eventos) == 0 && d >= primero && d <= ultimo && v.reglas.reservable(d), Turno: turno, EtiquetaTurno: etiqueta, Eventos: eventos, } } func (v Vista) Mes(anio, mes int) Mes { primero := fechaMes(anio, mes) total := diasDelMes(anio, mes) hueco := (int(primero.Weekday()) + 6) % 7 // celdas vacias antes del dia 1 m := Mes{Nombre: meses[mes-1], Anio: anio} semana := Semana{} for i := 0; i < hueco; i++ { semana.Dias = append(semana.Dias, Celda{Vacia: true}) } for dia := 1; dia <= total; dia++ { d := DiaDe(primero.AddDate(0, 0, dia-1)) semana.Dias = append(semana.Dias, v.celda(d, dia)) if len(semana.Dias) == 7 { m.Semanas = append(m.Semanas, semana) semana = Semana{} } } if len(semana.Dias) > 0 { for len(semana.Dias) < 7 { semana.Dias = append(semana.Dias, Celda{Vacia: true}) } m.Semanas = append(m.Semanas, semana) } return m } // Meses devuelve el mes que corre y los siguientes, que son los que se pueden // pedir. func (v Vista) Meses(cuantos int) []Mes { t := v.hoy.MustTime() var out []Mes for i := 0; i < cuantos; i++ { m := t.AddDate(0, i, 0) out = append(out, v.Mes(m.Year(), int(m.Month()))) } return out } // Proximas lista los eventos que vienen, para la tabla de debajo del mes. func (v Vista) Proximas() []Evento { var out []Evento for _, e := range v.eventos { if e.Estado.Ocupa() && e.Hasta >= v.hoy { out = append(out, e) } } return out } // etiquetaEstado distingue lo que ha pedido la asamblea de externas mientras // espera: una vez confirmado es un evento más. func etiquetaEstado(e Estado, externas bool) string { if externas && e == Solicitado { return "SOLICITADO EXTERNAS" } return e.Etiqueta() } func diasDelMes(anio, mes int) int { return fechaMes(anio, mes).AddDate(0, 1, -1).Day() } func fechaMes(anio, mes int) time.Time { return time.Date(anio, time.Month(mes), 1, 0, 0, 0, 0, time.Local) }