add style to the search result

This commit is contained in:
Rok Garbas 2020-04-10 10:13:50 +02:00
parent f19216fe5a
commit 40bd247ee9
Failed to generate hash of commit
4 changed files with 286 additions and 117 deletions

View file

@ -54,6 +54,7 @@ def get_packages(evaluation):
check=True,
)
packages = json.loads(result.stdout).items()
packages = list(packages)[:10]
def gen():
for attr_name, data in packages:
@ -87,7 +88,14 @@ def get_packages(evaluation):
)
for maintainer in data["meta"].get("maintainers", [])
]
platforms = [
type(platform) == str
and platform
or None
for platform in data["meta"].get("platforms", [])
]
yield dict(
id=attr_name,
attr_name=attr_name,
name=data["pname"],
version=data["version"],
@ -95,6 +103,7 @@ def get_packages(evaluation):
longDescription=data["meta"].get("longDescription", ""),
license=licenses,
maintainers=maintainers,
platforms=[i for i in platforms if i],
position=position,
homepage=data["meta"].get("homepage"),
)
@ -114,6 +123,7 @@ def get_options(evaluation):
if os.path.exists(options_file):
with open(options_file) as f:
options = json.load(f).items()
options = list(options)[:10]
def gen():
for name, option in options:
@ -123,6 +133,7 @@ def get_options(evaluation):
example.get("_type") == "literalExample":
example = str(example["text"])
yield dict(
id=name,
option_name=name,
description=option.get("description"),
type=option.get("type"),
@ -133,6 +144,7 @@ def get_options(evaluation):
return len(options), gen
def recreate_index(es, channel):
if es.indices.exists(f"{channel}-packages"):
es.indices.delete(index=f"{channel}-packages")
@ -162,6 +174,7 @@ def recreate_index(es, channel):
github=dict(type="text"),
),
),
platforms=dict(type="keyword"),
position=dict(type="text"),
homepage=dict(type="text"),
),

View file

@ -6,19 +6,33 @@ import Browser.Navigation as Nav exposing (Key)
import Html
exposing
( Html
, a
, button
, div
, footer
, form
, h1
, header
, img
, input
, li
, p
, pre
, table
, tbody
, td
, text
, th
, thead
, tr
, ul
)
import Html.Attributes
exposing
( class
, colspan
, href
, src
, type_
, value
)
@ -26,6 +40,7 @@ import Html.Events
exposing
( onClick
, onInput
, onSubmit
)
import Http
import Json.Decode as D
@ -33,6 +48,7 @@ import Json.Decode.Pipeline as DP
import Json.Encode as E
import RemoteData as R
import Url exposing (Url)
import Url.Builder as UrlBuilder
import Url.Parser as UrlParser
exposing
( (<?>)
@ -64,8 +80,9 @@ type alias Model =
type alias SearchModel =
{ query : String
{ query : Maybe String
, result : R.WebData SearchResult
, showDetailsFor : Maybe String
}
@ -117,6 +134,7 @@ type alias SearchResultPackage =
, longDescription : Maybe String
, licenses : List SearchResultPackageLicense
, maintainers : List SearchResultPackageMaintainer
, platforms : List String
, position : Maybe String
, homepage : Maybe String
}
@ -133,7 +151,7 @@ type alias SearchResultOption =
type alias SearchResultPackageLicense =
{ fullName : String
{ fullName : Maybe String
, url : Maybe String
}
@ -147,21 +165,77 @@ type alias SearchResultPackageMaintainer =
emptySearch : Page
emptySearch =
SearchPage { query = "", result = R.NotAsked }
SearchPage
{ query = Nothing
, result = R.NotAsked
, showDetailsFor = Nothing
}
init : Flags -> Url -> Key -> ( Model, Cmd Msg )
init flags url key =
( { key = key
, elasticsearchUrl = flags.elasticsearchUrl
, elasticsearchUsername = flags.elasticsearchUsername
, elasticsearchPassword = flags.elasticsearchPassword
, page = UrlParser.parse urlParser url |> Maybe.withDefault emptySearch
}
, Cmd.none
let
model =
{ key = key
, elasticsearchUrl = flags.elasticsearchUrl
, elasticsearchUsername = flags.elasticsearchUsername
, elasticsearchPassword = flags.elasticsearchPassword
, page = UrlParser.parse urlParser url |> Maybe.withDefault emptySearch
}
in
( model
, initPageCmd model model
)
initPageCmd : Model -> Model -> Cmd Msg
initPageCmd oldModel model =
let
makeRequest query =
Http.riskyRequest
{ method = "POST"
, headers =
[ Http.header "Authorization" ("Basic " ++ Base64.encode (model.elasticsearchUsername ++ ":" ++ model.elasticsearchPassword))
]
, url = model.elasticsearchUrl ++ "/nixos-unstable-packages/_search"
, body =
Http.jsonBody <|
E.object
[ ( "query"
, E.object
[ ( "match"
, E.object
[ ( "attr_name"
, E.object
[ ( "query", E.string query )
-- I'm not sure we need fuziness
--, ( "fuzziness", E.int 1 )
]
)
]
)
]
)
]
, expect = Http.expectJson (R.fromResult >> SearchQueryResponse) decodeResult
, timeout = Nothing
, tracker = Nothing
}
in
case oldModel.page of
SearchPage oldSearchModel ->
case model.page of
SearchPage searchModel ->
if (oldSearchModel.query == searchModel.query) && R.isSuccess oldSearchModel.result then
Cmd.none
else
searchModel.query
|> Maybe.map makeRequest
|> Maybe.withDefault Cmd.none
-- ---------------------------
-- URL Parsing and Routing
@ -182,13 +256,14 @@ urlParser : Parser (Page -> msg) msg
urlParser =
UrlParser.oneOf
[ UrlParser.map
(\q ->
(\query showDetailsFor ->
SearchPage
{ query = q |> Maybe.withDefault ""
{ query = query
, result = R.NotAsked
, showDetailsFor = showDetailsFor
}
)
(UrlParser.s "search" <?> UrlParserQuery.string "query")
(UrlParser.s "search" <?> UrlParserQuery.string "query" <?> UrlParserQuery.string "showDetailsFor")
]
@ -204,6 +279,7 @@ type Msg
| SearchPageInput String
| SearchQuerySubmit
| SearchQueryResponse (R.WebData SearchResult)
| SearchShowPackageDetails String
decodeResult : D.Decoder SearchResult
@ -255,6 +331,7 @@ decodeResultPackage =
|> DP.required "longDescription" (D.nullable D.string)
|> DP.required "license" (D.list decodeResultPackageLicense)
|> DP.required "maintainers" (D.list decodeResultPackageMaintainer)
|> DP.required "platforms" (D.list D.string)
|> DP.required "position" (D.nullable D.string)
|> DP.required "homepage" (D.nullable D.string)
@ -262,7 +339,7 @@ decodeResultPackage =
decodeResultPackageLicense : D.Decoder SearchResultPackageLicense
decodeResultPackageLicense =
D.map2 SearchResultPackageLicense
(D.field "fullName" D.string)
(D.field "fullName" (D.nullable D.string))
(D.field "url" (D.nullable D.string))
@ -285,44 +362,6 @@ decodeResultOption =
(D.field "source" D.string)
initPage : Model -> Cmd Msg
initPage model =
case model.page of
SearchPage searchModel ->
if searchModel.query == "" then
Cmd.none
else
Http.riskyRequest
{ method = "POST"
, headers =
[ Http.header "Authorization" ("Basic " ++ Base64.encode (model.elasticsearchUsername ++ ":" ++ model.elasticsearchPassword))
]
, url = model.elasticsearchUrl ++ "/nixos-unstable-packages/_search"
, body =
Http.jsonBody <|
E.object
[ ( "query"
, E.object
[ ( "match"
, E.object
[ ( "name"
, E.object
[ ( "query", E.string searchModel.query )
, ( "fuzziness", E.int 1 )
]
)
]
)
]
)
]
, expect = Http.expectJson (R.fromResult >> SearchQueryResponse) decodeResult
, timeout = Nothing
, tracker = Nothing
}
update : Msg -> Model -> ( Model, Cmd Msg )
update message model =
case message of
@ -340,18 +379,19 @@ update message model =
SearchPage
{ searchModel
| result =
if searchModel.query == "" then
R.NotAsked
case searchModel.query of
Just query ->
R.Loading
else
R.Loading
Nothing ->
R.NotAsked
}
newNewModel =
{ newModel | page = newPage }
in
( newNewModel
, initPage newNewModel
, initPageCmd newModel newNewModel
)
SearchPageInput query ->
@ -359,7 +399,7 @@ update message model =
| page =
case model.page of
SearchPage searchModel ->
SearchPage { searchModel | query = query }
SearchPage { searchModel | query = Just query }
}
, Cmd.none
)
@ -368,7 +408,7 @@ update message model =
case model.page of
SearchPage searchModel ->
( model
, Nav.pushUrl model.key <| "/search?query=" ++ searchModel.query
, Nav.pushUrl model.key <| createSearchUrl searchModel
)
SearchQueryResponse result ->
@ -382,6 +422,47 @@ update message model =
, Cmd.none
)
SearchShowPackageDetails showDetailsFor ->
case model.page of
SearchPage searchModel ->
let
newSearchModel =
{ searchModel
| showDetailsFor =
if searchModel.showDetailsFor == Just showDetailsFor then
Nothing
else
Just showDetailsFor
}
in
( model
, Nav.pushUrl model.key <| createSearchUrl newSearchModel
)
createSearchUrl : SearchModel -> String
createSearchUrl model =
[]
|> List.append
(model.query
|> Maybe.map
(\query ->
[ UrlBuilder.string "query" query ]
)
|> Maybe.withDefault []
)
|> List.append
(model.showDetailsFor
|> Maybe.map
(\x ->
[ UrlBuilder.string "showDetailsFor" x
]
)
|> Maybe.withDefault []
)
|> UrlBuilder.absolute [ "search" ]
-- ---------------------------
@ -391,27 +472,54 @@ update message model =
view : Model -> Html Msg
view model =
div [ class "container" ]
div []
[ header []
[ h1 [] [ text "NixOS Search" ]
[ div [ class "navbar navbar-static-top" ]
[ div [ class "navbar-inner" ]
[ div [ class "container" ]
[ a [ class "brand", href "https://search.nixos.org" ]
[ img [ src "https://nixos.org/logo/nix-wiki.png", class "logo" ] []
]
]
]
]
]
, div [ class "container main" ]
[ case model.page of
SearchPage searchModel ->
searchPage searchModel
, footer [] []
]
, case model.page of
SearchPage searchModel ->
searchPage searchModel
]
searchPage : SearchModel -> Html Msg
searchPage model =
div []
[ div []
[ input
[ type_ "text"
, onInput SearchPageInput
, value model.query
div [ class "search-page" ]
[ h1 [ class "page-header" ] [ text "Search for packages and options" ]
, div [ class "search-input" ]
[ form [ onSubmit SearchQuerySubmit ]
[ div [ class "input-append" ]
[ input
[ type_ "text"
, onInput SearchPageInput
, value <| Maybe.withDefault "" model.query
]
[]
, div [ class "btn-group" ]
[ button [ class "btn" ] [ text "Search" ]
--TODO: and option to select the right channel+version+evaluation
--, button [ class "btn" ] [ text "Loading ..." ]
--, div [ class "popover bottom" ]
-- [ div [ class "arrow" ] []
-- , div [ class "popover-title" ] [ text "Select options" ]
-- , div [ class "popover-content" ]
-- [ p [] [ text "Sed posuere consectetur est at lobortis. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum." ] ]
-- ]
]
]
]
[]
, button [ onClick SearchQuerySubmit ] [ text "Search" ]
]
, case model.result of
R.NotAsked ->
@ -421,26 +529,60 @@ searchPage model =
div [] [ text "Loading" ]
R.Success result ->
ul [] (searchPageResult result.hits)
searchPageResult model.showDetailsFor result.hits
R.Failure error ->
div [] [ text "Error!", pre [] [ text (Debug.toString error) ] ]
]
searchPageResult : SearchResultHits -> List (Html Msg)
searchPageResult result =
List.map searchPageResultItem result.hits
searchPageResult : Maybe String -> SearchResultHits -> Html Msg
searchPageResult showDetailsFor result =
div [ class "search-result" ]
[ table [ class "table table-hover" ]
[ thead []
[ tr []
[ th [] [ text "Attribute name" ]
, th [] [ text "Name" ]
, th [] [ text "Version" ]
, th [] [ text "Description" ]
]
]
, tbody [] <| List.concatMap (searchPageResultItem showDetailsFor) result.hits
]
]
searchPageResultItem : SearchResultItem -> Html Msg
searchPageResultItem item =
-- case item.source of
-- Package package ->
-- li [] [ text package.attr_name ]
-- Option option ->
-- li [] [ text option.option_name ]
li [] [ text <| Debug.toString item ]
searchPageResultItem : Maybe String -> SearchResultItem -> List (Html Msg)
searchPageResultItem showDetailsFor item =
case item.source of
Package package ->
let
packageDetails =
if Just item.id == showDetailsFor then
[ td [ colspan 4 ] [ text "works!" ] ]
else
[]
in
[ tr [ onClick <| SearchShowPackageDetails item.id ]
[ td [] [ text package.attr_name ]
, td [] [ text package.name ]
, td [] [ text package.version ]
, td [] [ text <| Maybe.withDefault "" package.description ]
]
]
++ packageDetails
Option option ->
[ tr
[]
[-- td [] [ text option.option_name ]
--, td [] [ text option.name ]
--, td [] [ text option.version ]
--, td [] [ text option.description ]
]
]

View file

@ -1,11 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Elm hotloading dev environment</title>
</head>
<body>
</body>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Elm hotloading dev environment</title>
<script type="text/javascript" src="https://nixos.org/js/jquery.min.js"></script>
<script type="text/javascript" src="https://nixos.org/js/jquery-ui.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script type="text/javascript" src="https://nixos.org/bootstrap/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://nixos.org/bootstrap/css/bootstrap.min.css" />
<link rel="stylesheet" href="https://nixos.org/bootstrap/css/bootstrap-responsive.min.css" />
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" />
<link rel="shortcut icon" type="image/png" href="https://nixos.org/favicon.png" />
</head>
<body>
</body>
</html>

View file

@ -1,30 +1,27 @@
@import '~purecss/build/pure.css';
body {
background-color: #ddd;
margin-top: 20px;
header .navbar a.brand {
line-height: 1.5em;
img.logo {
height: 1.5em;
margin-right: 0.5em;
}
}
.container {
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
header {
display: flex;
align-items: center;
img {
width: 60px;
margin-right: 30px;
.search-page {
.search-input {
text-align: center;
.input-append input {
font-size: 24px;
height: 40px;
}
}
button {
margin-right: 15px;
}
.logo {
background: url('images/logo.png');
width: 60px;
height: 60px;
background-size: cover;
.input-append button {
font-size: 24px;
height: 50px;
}
}
.search-result {
tbody tr {
cursor: pointer;
}
}
}