Cleanups. Adding Speaker detail.

This commit is contained in:
Vidir Valberg Gudmundsson 2017-08-02 22:20:38 +02:00
parent a447ca476f
commit d4d7fad439
13 changed files with 177 additions and 44 deletions

View file

@ -44,6 +44,10 @@ speakerDecoder : Decoder Speaker
speakerDecoder =
decode Speaker
|> required "name" string
|> required "slug" string
|> required "biography" string
|> optional "large_picture_url" (nullable string) Nothing
|> optional "small_picture_url" (nullable string) Nothing
eventDecoder : Decoder Event
@ -52,7 +56,7 @@ eventDecoder =
|> required "title" string
|> required "slug" string
|> required "abstract" string
|> required "speakers" (list speakerDecoder)
|> required "speaker_slugs" (list string)
|> required "video_recording" bool
|> optional "video_url" string ""
|> required "event_type" string
@ -115,3 +119,4 @@ initDataDecoder =
|> required "event_instances" (list eventInstanceDecoder)
|> required "event_locations" (list eventLocationDecoder)
|> required "event_types" (list eventTypeDecoder)
|> required "speakers" (list speakerDecoder)

View file

@ -37,7 +37,7 @@ init flags location =
Filter [] [] []
model =
Model [] [] [] [] [] flags emptyFilter location currentRoute False
Model [] [] [] [] [] [] flags emptyFilter location currentRoute False
in
model ! [ sendInitMessage flags.camp_slug flags.websocket_server ]

View file

@ -2,7 +2,7 @@ module Messages exposing (Msg(..))
-- Local modules
import Models exposing (Day, EventType, EventLocation, EventInstance)
import Models exposing (Day, EventType, EventLocation, EventInstance, VideoRecordingFilter)
-- External modules
@ -15,6 +15,6 @@ type Msg
| WebSocketPayload String
| ToggleEventTypeFilter EventType
| ToggleEventLocationFilter EventLocation
| ToggleVideoRecordingFilter { name : String, slug : String, filter : EventInstance -> Bool }
| ToggleVideoRecordingFilter VideoRecordingFilter
| OnLocationChange Location
| BackInHistory

View file

@ -10,11 +10,36 @@ import Date exposing (Date, now)
import Navigation exposing (Location)
type alias EventSlug =
String
type alias EventInstanceSlug =
String
type alias SpeakerSlug =
String
type alias DaySlug =
String
type alias FilterQuery =
String
-- Route is defined here rather than in Routing.elm due to it being used in Model. If it were in Routing.elm we would have a circular dependency.
type Route
= OverviewRoute
| OverviewFilteredRoute String
| DayRoute String
| OverviewFilteredRoute FilterQuery
| DayRoute DaySlug
| EventRoute EventSlug
| SpeakerRoute SpeakerSlug
| NotFoundRoute
@ -24,6 +49,7 @@ type alias Model =
, eventInstances : List EventInstance
, eventLocations : List EventLocation
, eventTypes : List EventType
, speakers : List Speaker
, flags : Flags
, filter : Filter
, location : Location
@ -35,10 +61,14 @@ type alias Model =
type alias Filter =
{ eventTypes : List EventType
, eventLocations : List EventLocation
, videoRecording : List { name : String, slug : String, filter : EventInstance -> Bool }
, videoRecording : List VideoRecordingFilter
}
type alias VideoRecordingFilter =
{ name : String, slug : String, filter : EventInstance -> Bool }
type alias Day =
{ day_name : String
, date : Date
@ -48,17 +78,13 @@ type alias Day =
type alias Speaker =
{ name : String
, slug : SpeakerSlug
, biography : String
, largePictureUrl : Maybe String
, smallPictureUrl : Maybe String
}
type alias EventSlug =
String
type alias EventInstanceSlug =
String
type alias EventInstance =
{ title : String
, slug : EventInstanceSlug
@ -83,7 +109,7 @@ type alias Event =
{ title : String
, slug : EventSlug
, abstract : String
, speakers : List Speaker
, speakerSlugs : List SpeakerSlug
, videoRecording : Bool
, videoUrl : String
, eventType : String

View file

@ -14,16 +14,16 @@ import UrlParser exposing (Parser, (</>), oneOf, map, top, s, string, parseHash)
{--
URLs to support:
- #
- #/
This show the overview of the schedule
- #?type={types},location={locations},video={no,yes,link}
- #/?type={types},location={locations},video={not-to-be-recorded,to-be-recorded,has-recording}
This is the overview, just with filters enable
- #day/{year}-{month}-{day}
- #/day/{year}-{month}-{day}
Show a particular day
- #event/{slug}
- #/event/{slug}
Show a particular event
--}
@ -36,6 +36,7 @@ matchers =
, map OverviewFilteredRoute (top </> string)
, map DayRoute (s "day" </> string)
, map EventRoute (s "event" </> string)
, map SpeakerRoute (s "speaker" </> string)
]
@ -43,3 +44,29 @@ parseLocation : Location -> Route
parseLocation location =
parseHash matchers location
|> Maybe.withDefault NotFoundRoute
routeToString : Route -> String
routeToString route =
let
parts =
case route of
OverviewRoute ->
[]
OverviewFilteredRoute query ->
[ query ]
DayRoute iso ->
[ "day", iso ]
EventRoute slug ->
[ "event", slug ]
SpeakerRoute slug ->
[ "speaker", slug ]
NotFoundRoute ->
[]
in
"#/" ++ String.join "/" parts

View file

@ -7,6 +7,7 @@ import Messages exposing (Msg(..))
import Views.DayPicker exposing (dayPicker)
import Views.DayView exposing (dayView)
import Views.EventDetail exposing (eventDetailView)
import Views.SpeakerDetail exposing (speakerDetailView)
import Views.ScheduleOverview exposing (scheduleOverviewView)
@ -48,6 +49,9 @@ view model =
EventRoute eventSlug ->
eventDetailView eventSlug model
SpeakerRoute speakerSlug ->
speakerDetailView speakerSlug model
NotFoundRoute ->
div [] [ text "Not found!" ]
]

View file

@ -4,6 +4,7 @@ module Views.DayPicker exposing (..)
import Models exposing (..)
import Messages exposing (Msg(..))
import Routing exposing (routeToString)
-- Core modules
@ -51,7 +52,7 @@ dayPicker model =
, ( "btn-default", not isAllDaysActive )
, ( "btn-primary", isAllDaysActive )
]
, href ("#")
, href <| routeToString OverviewRoute
]
[ text "All Days"
]
@ -78,7 +79,7 @@ dayButton day activeDate =
, ( "btn-default", not isActive )
, ( "btn-primary", isActive )
]
, href ("#day/" ++ (Date.Extra.toFormattedString "y-MM-dd" day.date))
, href <| routeToString <| DayRoute <| Date.Extra.toFormattedString "y-MM-dd" day.date
]
[ text day.day_name
]

View file

@ -3,7 +3,8 @@ module Views.DayView exposing (dayView)
-- Local modules
import Messages exposing (Msg(..))
import Models exposing (Model, Day, EventInstance, EventLocation)
import Models exposing (Model, Day, EventInstance, EventLocation, Route(EventRoute))
import Routing exposing (routeToString)
-- Core modules
@ -236,7 +237,7 @@ eventInstanceBlock offset numberInGroup ( eventInstance, lefts ) =
, ( "background-color", eventInstance.backgroundColor )
, ( "color", eventInstance.forgroundColor )
]
, href ("#event/" ++ eventInstance.eventSlug)
, href <| routeToString <| EventRoute eventInstance.eventSlug
]
[ p [] [ text ((Date.Extra.toFormattedString "HH:mm" eventInstance.from) ++ " " ++ eventInstance.title) ]
]

View file

@ -4,6 +4,7 @@ module Views.EventDetail exposing (eventDetailView)
import Messages exposing (Msg(..))
import Models exposing (..)
import Routing exposing (routeToString)
-- Core modules
@ -27,15 +28,12 @@ eventDetailView eventSlug model =
model.events
|> List.filter (\e -> e.slug == eventSlug)
|> List.head
eventInstances =
List.filter (\instance -> instance.eventSlug == eventSlug) model.eventInstances
in
case event of
Just event ->
div [ class "row" ]
[ eventDetailContent event
, eventDetailSidebar event eventInstances
, eventDetailSidebar event model
]
Nothing ->
@ -53,12 +51,52 @@ eventDetailContent event =
, text " Back"
]
, h3 [] [ text event.title ]
, p [] [ Markdown.toHtml [] event.abstract ]
, div [] [ Markdown.toHtml [] event.abstract ]
]
eventDetailSidebar : Event -> List EventInstance -> Html Msg
eventDetailSidebar event eventInstances =
getSpeakersFromSlugs : List Speaker -> List SpeakerSlug -> List Speaker -> List Speaker
getSpeakersFromSlugs speakers slugs collectedSpeakers =
case speakers of
[] ->
collectedSpeakers
speaker :: rest ->
let
foundSlug =
slugs
|> List.filter (\slug -> slug == speaker.slug)
|> List.head
foundSpeaker =
case foundSlug of
Just slug ->
[ speaker ]
Nothing ->
[]
newSlugs =
case foundSlug of
Just slug ->
List.filter (\x -> x /= slug) slugs
Nothing ->
slugs
newCollectedSpeakers =
collectedSpeakers ++ foundSpeaker
in
case slugs of
[] ->
collectedSpeakers
_ ->
getSpeakersFromSlugs rest newSlugs newCollectedSpeakers
eventDetailSidebar : Event -> Model -> Html Msg
eventDetailSidebar event model =
let
videoRecordingLink =
case event.videoUrl of
@ -71,6 +109,12 @@ eventDetailSidebar event eventInstances =
, text " Watch recording here!"
]
]
eventInstances =
List.filter (\instance -> instance.eventSlug == event.slug) model.eventInstances
speakers =
getSpeakersFromSlugs model.speakers event.speakerSlugs []
in
div
[ classList
@ -80,7 +124,7 @@ eventDetailSidebar event eventInstances =
]
]
(videoRecordingLink
++ [ speakerSidebar event.speakers
++ [ speakerSidebar speakers
, eventMetaDataSidebar event
, eventInstancesSidebar eventInstances
]
@ -121,7 +165,7 @@ speakerSidebar speakers =
speakerDetail : Speaker -> Html Msg
speakerDetail speaker =
li []
[ text speaker.name
[ a [ href <| routeToString <| SpeakerRoute speaker.slug ] [ text speaker.name ]
]

View file

@ -3,7 +3,8 @@ module Views.FilterView exposing (filterSidebar, applyFilters, parseFilterFromQu
-- Local modules
import Messages exposing (Msg(..))
import Models exposing (Model, EventInstance, Filter, Day)
import Models exposing (Model, EventInstance, Filter, Day, FilterQuery, Route(OverviewFilteredRoute), VideoRecordingFilter)
import Routing exposing (routeToString)
-- Core modules
@ -14,7 +15,7 @@ import Regex
-- External modules
import Html exposing (Html, text, div, ul, li, span, i, h4)
import Html.Attributes exposing (class, classList, href)
import Html.Attributes exposing (class, classList)
import Html.Events exposing (onClick)
import Date.Extra exposing (Interval(..), equalBy)
@ -95,7 +96,7 @@ hasRecordingFilter eventInstance =
eventInstance.videoUrl /= ""
videoRecordingFilters : List { name : String, slug : String, filter : EventInstance -> Bool }
videoRecordingFilters : List VideoRecordingFilter
videoRecordingFilters =
[ { name = "Will not be recorded", slug = "not-to-be-recorded", filter = notRecordedFilter }
, { name = "Will recorded", slug = "to-be-recorded", filter = recordedFilter }
@ -177,7 +178,7 @@ getFilter filterType modelItems query =
List.filterMap (\x -> findFilter modelItems x) filterSlugs
parseFilterFromQuery : String -> Model -> Filter
parseFilterFromQuery : FilterQuery -> Model -> Filter
parseFilterFromQuery query model =
let
types =
@ -195,7 +196,7 @@ parseFilterFromQuery query model =
}
filterToQuery : Filter -> String
filterToQuery : Filter -> FilterQuery
filterToQuery filter =
let
typePart =
@ -225,4 +226,4 @@ filterToQuery filter =
result =
String.join "&" (List.filter (\x -> x /= "") [ typePart, locationPart, videoPart ])
in
"#" ++ result
routeToString <| OverviewFilteredRoute result

View file

@ -3,8 +3,9 @@ module Views.ScheduleOverview exposing (scheduleOverviewView)
-- Local modules
import Messages exposing (Msg(..))
import Models exposing (Model, Day, EventInstance, Filter)
import Models exposing (Model, Day, EventInstance, Filter, Route(EventRoute))
import Views.FilterView exposing (filterSidebar, applyFilters, parseFilterFromQuery)
import Routing exposing (routeToString)
-- External modules
@ -50,7 +51,7 @@ dayEventInstanceView eventInstance =
[ ( "event", True )
, ( "event-in-overview", True )
]
, href ("#event/" ++ eventInstance.eventSlug)
, href <| routeToString <| EventRoute eventInstance.eventSlug
, style
[ ( "background-color", eventInstance.backgroundColor )
, ( "color", eventInstance.forgroundColor )

View file

@ -1,7 +1,7 @@
from channels.generic.websockets import JsonWebsocketConsumer
from camps.models import Camp
from .models import Event, EventInstance, Favorite, EventLocation, EventType
from .models import Event, EventInstance, Favorite, EventLocation, EventType, Speaker
class ScheduleConsumer(JsonWebsocketConsumer):
@ -41,12 +41,16 @@ class ScheduleConsumer(JsonWebsocketConsumer):
event_types_query_set = EventType.objects.filter()
event_types = list([x.serialize() for x in event_types_query_set])
speakers_query_set = Speaker.objects.filter(camp=camp)
speakers = list([x.serialize() for x in speakers_query_set])
data = {
"action": "init",
"events": events,
"event_instances": event_instances,
"event_locations": event_locations,
"event_types": event_types,
"speakers": speakers,
"days": days,
}
except Camp.DoesNotExist:

View file

@ -14,6 +14,8 @@ from django.dispatch import receiver
from django.utils.text import slugify
from django.conf import settings
from django.core.urlresolvers import reverse_lazy
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse_lazy, reverse
from django.core.files.storage import FileSystemStorage
from django.urls import reverse
from django.apps import apps
@ -433,8 +435,8 @@ class Event(CampRelatedModel):
'title': self.title,
'slug': self.slug,
'abstract': self.abstract,
'speakers': [
speaker.serialize()
'speaker_slugs': [
speaker.slug
for speaker in self.speakers.all()
],
'video_recording': self.video_recording,
@ -613,10 +615,27 @@ class Speaker(CampRelatedModel):
def get_absolute_url(self):
return reverse_lazy('speaker_detail', kwargs={'camp_slug': self.camp.slug, 'slug': self.slug})
def get_picture_url(self, size):
return reverse('speaker_picture', kwargs={'camp_slug': self.camp.slug, 'slug': self.slug, 'picture': size})
def get_small_picture_url(self):
return self.get_picture_url('thumbnail')
def get_large_picture_url(self):
return self.get_picture_url('large')
def serialize(self):
data = {
'name': self.name,
'slug': self.slug,
'biography': self.biography,
}
if self.picture_small and self.picture_large:
data['large_picture_url'] = self.get_large_picture_url()
data['small_picture_url'] = self.get_small_picture_url()
return data