Skip to content
This repository was archived by the owner on Apr 14, 2025. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 50 additions & 19 deletions internal/app/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (app *Application) getCodeSystemByID(w http.ResponseWriter, r *http.Request
rp := app.repository

id := r.PathValue("id")
id_type, err := determineIdType(id)
id_type, err := determineParamType(id)
if err != nil {
customErrors.BadRequest(w, r, err, app.logger)
return
Expand Down Expand Up @@ -81,7 +81,7 @@ func (app *Application) getFHIRCodeSystemByID(w http.ResponseWriter, r *http.Req
rp := app.repository

id := r.PathValue("id")
id_type, err := determineIdType(id)
id_type, err := determineParamType(id)
if err != nil {
customErrors.BadRequest(w, r, err, app.logger)
return
Expand Down Expand Up @@ -285,7 +285,7 @@ func (app *Application) getValueSetByID(w http.ResponseWriter, r *http.Request)
rp := app.repository

id := r.PathValue("id")
id_type, err := determineIdType(id)
id_type, err := determineParamType(id)
if err != nil {
customErrors.BadRequest(w, r, err, app.logger)
return
Expand Down Expand Up @@ -542,27 +542,56 @@ func (app *Application) search(w http.ResponseWriter, r *http.Request, searchTer
rp := app.repository
logger := app.logger

result := &models.CodeSystemResultRow{}
result := &models.SearchResultRow{}
defaultPageCount := 5

// retrieve code system
codeSystem, err := rp.GetCodeSystemsByLikeOID(r.Context(), searchTerm)
if err != nil || len(*codeSystem) < 1 {
if err == nil {
err = sql.ErrNoRows
lookupType, err := determineParamType(searchTerm)

if searchType == "value_set" || searchType == "all" {
var valueSets *[]xo.ValueSet
valueSets, err = rp.SearchValueSets(r.Context(), searchTerm, lookupType)
fmt.Println(len(*valueSets))
if err != nil || len(*valueSets) < 1 {
if err == nil {
err = sql.ErrNoRows
}
customErrors.SearchError(w, r, err, searchTerm, searchType, logger)
return
}
for _, vs := range *valueSets {
result.ValueSets = append(result.ValueSets, &vs)
}
customErrors.SearchError(w, r, err, searchTerm, logger)
return
}

for _, cs := range *codeSystem {
result.CodeSystems = append(result.CodeSystems, &cs)
}
if len(result.ValueSets) <= defaultPageCount {
defaultPageCount = len(result.ValueSets)
}

if len(result.CodeSystems) <= defaultPageCount {
defaultPageCount = len(result.CodeSystems)
}
result.PageCount = defaultPageCount
result.ValueSetsCount = strconv.Itoa(len(result.ValueSets))
} else if searchType == "code_system" {
var codeSystems *[]xo.CodeSystem
codeSystems, err = rp.SearchCodeSystems(r.Context(), searchTerm, lookupType)

// retrieve code system
if err != nil || len(*codeSystems) < 1 {
if err == nil {
err = sql.ErrNoRows
}
customErrors.SearchError(w, r, err, searchTerm, searchType, logger)
return
}

for _, cs := range *codeSystems {
result.CodeSystems = append(result.CodeSystems, &cs)
}

if len(result.CodeSystems) <= defaultPageCount {
defaultPageCount = len(result.CodeSystems)
}
} else {
err = errors.New("Under Construction...")
customErrors.SearchError(w, r, err, searchTerm, searchType, logger)
}
result.PageCount = defaultPageCount
result.CodeSystemsCount = strconv.Itoa(len(result.CodeSystems))

Expand All @@ -577,7 +606,9 @@ func (app *Application) search(w http.ResponseWriter, r *http.Request, searchTer
result.CodeSystemConceptsCount = strconv.Itoa(0)

// for now
result.ValueSetsCount = strconv.Itoa(0)
if result.ValueSetsCount == "" {
result.ValueSetsCount = strconv.Itoa(0)
}

w.Header().Set("HX-Push-Url", fmt.Sprintf("/search?type=%s&input=%s", searchType, searchTerm))

Expand Down
11 changes: 7 additions & 4 deletions internal/app/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ import (
"github.com/CDCgov/phinvads-go/internal/errors"
)

func determineIdType(input string) (output string, err error) {
validId, _ := regexp.MatchString("^[a-zA-Z0-9-]+$", input)
func determineParamType(input string) (output string, err error) {
validUuid, _ := regexp.MatchString("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", input)
validOid, _ := regexp.MatchString("^[0-9.]+$", input)
validString, _ := regexp.MatchString("^[a-zA-Z0-9 ]*$", input)

if validId {
return "id", nil
if validUuid {
return "uuid", nil
} else if validOid {
return "oid", nil
} else if validString {
return "string", nil
} else {
return "", errors.ErrInvalidId
}
Expand Down
24 changes: 22 additions & 2 deletions internal/database/models/codesystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,27 @@ func GetAllCodeSystems(ctx context.Context, db xo.DB) (*[]xo.CodeSystem, error)
}

func GetCodeSystemByLikeOID(ctx context.Context, db xo.DB, oid string) (*[]xo.CodeSystem, error) {
wildcard := oid + "%"
wildcard := "%" + oid + "%"
const sqlstr = "SELECT * FROM public.code_system WHERE oid LIKE $1"

return queryCodeSystems(ctx, db, sqlstr, wildcard)
}

func GetCodeSystemByLikeID(ctx context.Context, db xo.DB, id string) (*[]xo.CodeSystem, error) {
wildcard := "%" + id + "%"
const sqlstr = "SELECT * FROM public.code_system WHERE id LIKE $1"

return queryCodeSystems(ctx, db, sqlstr, wildcard)
}

func GetCodeSystemByLikeString(ctx context.Context, db xo.DB, input string) (*[]xo.CodeSystem, error) {
wildcard := "%" + input + "%"
const sqlstr = "SELECT * FROM public.code_system WHERE lower(name) LIKE lower($1) OR lower(codesystemcode) LIKE lower ($1)"

return queryCodeSystems(ctx, db, sqlstr, wildcard)
}

func queryCodeSystems(ctx context.Context, db xo.DB, sqlstr, wildcard string) (*[]xo.CodeSystem, error) {
codeSystems := []xo.CodeSystem{}
rows, err := db.QueryContext(ctx, sqlstr, wildcard)
if err != nil {
Expand All @@ -45,13 +63,15 @@ func GetCodeSystemByLikeOID(ctx context.Context, db xo.DB, oid string) (*[]xo.Co
return &codeSystems, nil
}

type CodeSystemResultRow struct {
type SearchResultRow struct {
CodeSystemsCount string
CodeSystemConceptsCount string
ValueSetsCount string
ValueSetConceptsCount string
CodeSystems []*xo.CodeSystem
CodeSystemConcepts []*xo.CodeSystemConcept
ValueSets []*xo.ValueSet
ValueSetConcepts []*xo.ValueSet
URL string
PageCount int
}
Expand Down
20 changes: 18 additions & 2 deletions internal/database/models/repository/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,14 @@ func (r *Repository) GetCodeSystemByOID(ctx context.Context, oid string) (*xo.Co
return xo.CodeSystemByOid(ctx, r.database, oid)
}

func (r *Repository) GetCodeSystemsByLikeOID(ctx context.Context, oid string) (*[]xo.CodeSystem, error) {
return models.GetCodeSystemByLikeOID(ctx, r.database, oid)
func (r *Repository) SearchCodeSystems(ctx context.Context, searchTerm, lookupType string) (*[]xo.CodeSystem, error) {
if lookupType == "uuid" {
return models.GetCodeSystemByLikeID(ctx, r.database, searchTerm)
} else if lookupType == "oid" {
return models.GetCodeSystemByLikeOID(ctx, r.database, searchTerm)
} else {
return models.GetCodeSystemByLikeString(ctx, r.database, searchTerm)
}
}

// =============================== //
Expand Down Expand Up @@ -82,6 +88,16 @@ func (r *Repository) GetValueSetByVersionOID(ctx context.Context, vsv *xo.ValueS
return vsv.ValueSet(ctx, r.database)
}

func (r *Repository) SearchValueSets(ctx context.Context, searchTerm, lookupType string) (*[]xo.ValueSet, error) {
if lookupType == "uuid" {
return models.GetValueSetByLikeID(ctx, r.database, searchTerm)
} else if lookupType == "oid" {
return models.GetValueSetByLikeOID(ctx, r.database, searchTerm)
} else {
return models.GetValueSetByLikeString(ctx, r.database, searchTerm)
}
}

// =============================== //
// ========= View methods ======== //
// =============================== //
Expand Down
38 changes: 38 additions & 0 deletions internal/database/models/valueset.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,41 @@ func GetAllValueSets(ctx context.Context, db xo.DB) (*[]xo.ValueSet, error) {
}
return &valueSets, nil
}

func GetValueSetByLikeOID(ctx context.Context, db xo.DB, oid string) (*[]xo.ValueSet, error) {
wildcard := "%" + oid + "%"
const sqlstr = "SELECT * FROM public.value_set WHERE oid LIKE $1"

return queryValueSets(ctx, db, sqlstr, wildcard)
}

func GetValueSetByLikeID(ctx context.Context, db xo.DB, id string) (*[]xo.ValueSet, error) {
wildcard := "%" + id + "%"
const sqlstr = "SELECT * FROM public.value_set WHERE id LIKE $1"

return queryValueSets(ctx, db, sqlstr, wildcard)
}

func GetValueSetByLikeString(ctx context.Context, db xo.DB, input string) (*[]xo.ValueSet, error) {
wildcard := "%" + input + "%"
const sqlstr = "SELECT * FROM public.value_set WHERE lower(name) LIKE lower($1) OR lower(code) LIKE lower($1);"

return queryValueSets(ctx, db, sqlstr, wildcard)
}

func queryValueSets(ctx context.Context, db xo.DB, sqlstr, wildcard string) (*[]xo.ValueSet, error) {
valueSets := []xo.ValueSet{}
rows, err := db.QueryContext(ctx, sqlstr, wildcard)
if err != nil {
return nil, err
}
for rows.Next() {
vs := xo.ValueSet{}
err := rows.Scan(&vs.Oid, &vs.ID, &vs.Name, &vs.Code, &vs.Status, &vs.Definitiontext, &vs.Scopenotetext, &vs.Assigningauthorityid, &vs.Legacyflag, &vs.Statusdate)
if err != nil {
return nil, err
}
valueSets = append(valueSets, vs)
}
return &valueSets, nil
}
6 changes: 3 additions & 3 deletions internal/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ func LogError(w http.ResponseWriter, r *http.Request, err error, errrorText stri
logger.Error(err.Error(), slog.String("method", r.Method), slog.String("uri", r.URL.RequestURI()))
}

func SearchError(w http.ResponseWriter, r *http.Request, err error, searchTerm string, logger *slog.Logger) {
func SearchError(w http.ResponseWriter, r *http.Request, err error, searchTerm, searchType string, logger *slog.Logger) {
if errors.Is(err, sql.ErrNoRows) {
errorString := fmt.Sprintf("Error: Code System %s not found", searchTerm)
errorString := fmt.Sprintf("Error: no matching resources found for '%s'", searchTerm)
dbErr := &DatabaseError{
Err: err,
Msg: errorString,
Expand All @@ -73,7 +73,7 @@ func SearchError(w http.ResponseWriter, r *http.Request, err error, searchTerm s
} else {
LogError(w, r, err, http.StatusText(http.StatusInternalServerError), logger)

component := components.Error("search", err.Error())
component := components.Error("Search", err.Error())
component.Render(r.Context(), w)
}
}
Expand Down
2 changes: 1 addition & 1 deletion internal/ui/components/code_system_result.templ
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"github.com/CDCgov/phinvads-go/internal/database/models"
)

templ CodeSystemResult(searchTerm string, searchResults *models.CodeSystemResultRow) {
templ CodeSystemResult(searchTerm string, searchResults *models.SearchResultRow) {
<div>
@CodeSystemResultsCount(searchResults)
<div class="search-results__table" role="table" aria-label="table">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package components

import "github.com/CDCgov/phinvads-go/internal/database/models"

templ CodeSystemResultsCount(result *models.CodeSystemResultRow) {
templ CodeSystemResultsCount(result *models.SearchResultRow) {
<div class="search-results__tabs">
<div
if result.ValueSetsCount == "0" && result.CodeSystemsCount != "0" {
Expand Down
6 changes: 5 additions & 1 deletion internal/ui/components/search_results.templ
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package components
import "github.com/CDCgov/phinvads-go/internal/database/models"


templ SearchResults(isDirect bool, currentPage, searchTerm string, searchResults *models.CodeSystemResultRow) {
templ SearchResults(isDirect bool, currentPage, searchTerm string, searchResults *models.SearchResultRow) {
@Base(currentPage) {
<h1>
Search
Expand All @@ -12,7 +12,11 @@ templ SearchResults(isDirect bool, currentPage, searchTerm string, searchResults
<h2>
Results: "{searchTerm}"
</h2>
if searchResults.ValueSetsCount != "0" {
@ValueSetResult(searchTerm, searchResults)
} else {
@CodeSystemResult(searchTerm, searchResults)
}
<div><br></div>
}
}
64 changes: 64 additions & 0 deletions internal/ui/components/value_set_result.templ
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package components

import (
"strconv"
"github.com/CDCgov/phinvads-go/internal/database/models"
)

templ ValueSetResult(searchTerm string, searchResults *models.SearchResultRow) {
<div>
@ValueSetResultsCount(searchResults)
<div class="search-results__table" role="table" aria-label="table">
<div role="rowgroup">
<div class="cdc-header top-header">
<div class="col search-results page-count">
Showing {strconv.Itoa(searchResults.PageCount)} of {searchResults.ValueSetsCount} Value Sets
</div>
<div
if searchResults.PageCount >=5 {
class="pagination"
} else {
class="pagination"
hidden
}
>
<div class="col search-results page-buttons">
<button class="active page-button">1</button>
<button class="page-button">2</button>
<button class="page-button">3</button>
<button class="page-button">4</button>
<button class="page-button">5</button>
<div class="button-next">
<button class="page-button">Next</button>
</div>
</div>
</div>
<div class="col search-results download-button">
if searchResults.ValueSetsCount == "0" {
<button disabled aria-disabled="true">
<img src="/assets/img/material-icons/file_download_off.svg">
Download Value Set
</button>
} else {
<button>
<img src="/assets/img/material-icons/file_download.svg">
Download Value Set
</button>
}
</div>
</div>
</div>
<table>
@ValueSetTableHeader()
<div role="rowgroup">
for idx, item := range searchResults.ValueSets {
if idx <= 4 {
// modulo check allows alternating background color
@ValueSetResultRow(idx % 2 != 0, item)
}
}
</div>
</table>
</div>
</div>
}
Loading