// Returns configuration and runtime information about the deployed application. func GetSystemInfo(mgr *server.EntityManager, cfg AppConfig) webapp.HttpHandler { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") contentBuffer := mgr.ContentBuffer() entityStats := contentBuffer.LatestEntityStats() // If no content received from the content source within the last minute // or so, we deem it "offline". timeSinceLatestContentReceived := time.Since(entityStats.NewestContent.Time()) isContentSourceOnline := timeSinceLatestContentReceived <= (1 * time.Minute) timeRangesInHours := []int{} for _, duration := range cfg.TimeRanges { timeRangesInHours = append(timeRangesInHours, int(duration/time.Hour)) } itemCounts := map[string]int{ "Documents": contentBuffer.DocumentCount(), "Persons": contentBuffer.PersonGraph.EntityCount(), "Orgs": contentBuffer.OrgGraph.EntityCount(), "Places": contentBuffer.PlaceGraph.EntityCount(), } runtimeInfo := map[string]interface{}{ "Errors": mgr.RecentErrors(), "ItemCounts": itemCounts, "ContentSourceOnline": isContentSourceOnline, "TimeRangesInHours": timeRangesInHours, "OldestContent": entityStats.OldestContent.String(), "NewestContent": entityStats.NewestContent.String(), } deploymentInfo := map[string]interface{}{ "Version": GIT_VERSION, "BuildDate": BUILD_DATE, "MemDbEndpoint": cfg.MemDbConn, } systemIinfo := map[string]interface{}{ "Runtime": runtimeInfo, "Deployment": deploymentInfo, } sendJsonResponse(systemIinfo, w) } }
// Saves the global data (a.k.a. the "content buffer") to disk. func SaveGlobalData(entityMgr *server.EntityManager, cfg AppConfig) webapp.HttpHandler { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") logger.Printf("\n\n========================\n" + "SAVING GLOBAL DATA!\n" + "========================\n\n") dataDir := cfg.DataDir createDataDirIfNotExists(dataDir) refreshStatsLock.Lock() entityMgr.ContentBuffer().SaveState(dataDir) entityMgr.ContentDAO.Save(dataDir) refreshStatsLock.Unlock() logger.Printf("\n\n========================\n" + "GLOBAL DATA SAVED!\n" + "========================\n\n") } }
// PA-241/PA-198: Support for disjunctive querying. func GetAllEntityInfo(mgr *server.EntityManager) webapp.UserHttpHandler { return func(w http.ResponseWriter, r *http.Request, userId int) { w.Header().Set("Content-Type", "application/json") entityStr2entityType := map[string]server.EntityType{ "Person": server.PersonEntity, "Org": server.OrgEntity, "Place": server.PlaceEntity, } // This function calculates the 'AND' co-occurence of the conjunct expression // of the form: ["and", "<entityType>:<id>", "<entityType:id", ...] calcConjunctExpr := func(g *server.ContentBuffer, expr ConjunctiveExpr) (*server.ContentBuffer, error) { logger.Printf("Calculating co-occurences for conjuncts: %v", expr.And) for _, entity := range expr.And { logger.Printf("Filtering on %v", entity.Id) entityTypeStr, entityId, err := parseEntityStr(entity.Id) if err != nil { return nil, err } entityType, isValidEntityType := entityStr2entityType[entityTypeStr] if !isValidEntityType { return nil, errors.New(fmt.Sprintf("Unknown entity type: '%v'", entityTypeStr)) } g = g.FilterOnEntity(entityType, entityId) } return g, nil } // Parse the posted data into the filter query in JSON format. // This query is essentially a LISP expression of the form: // // (op arg1 arg2 ...) // // where op is (for now) an 'or' operator (i.e. a disjunction). // Each arg is a nested LISP conjunct expression of the form (and arg1 arg2...), // where each arg is a string representing a distinct entity of the form // "<entity type>:<entity id>" (e.g. "Person:9283742"). getHttpRequestBody(w, r, func(postedData []byte) { var stats server.EntityStats var err error var finalContentBuffer *server.ContentBuffer // Default filter query: no time range specified, and no entity filters specified. filterQuery := FilterQuery{} httpRequestContainsData := !strutil.IsEmpty(string(postedData)) if httpRequestContainsData { if err = json.Unmarshal(postedData, &filterQuery); err != nil { http.Error(w, fmt.Sprintf("User %v: Error parsing posted JSON: %v", userId, err), http.StatusBadRequest) return } } var baseContentBuffer *server.ContentBuffer if filterQuery.IsTimeRangeSpecified() { timeRange := time.Duration(filterQuery.TimeRangeInHours) * time.Hour logger.Printf("Getting content buffer for timeRange=%v", timeRange) baseContentBuffer = mgr.ContentBufferForTimeRange(timeRange) } else { logger.Printf("Getting global content buffer") baseContentBuffer = mgr.ContentBuffer() } if filterQuery.IsEntityFilterSpecified() { logger.Printf("Calculating entity co-occurrences with disjunct query: %+V", filterQuery.Or) tmpContentBuffer := baseContentBuffer finalContentBuffer = server.NewContentBuffer() // Each disjunct is itself a conjunct expression of the form (and arg1 arg2 ...) for _, conjunctiveExpr := range filterQuery.Or { tmpContentBuffer, err = calcConjunctExpr(baseContentBuffer, conjunctiveExpr) if err != nil { http.Error(w, fmt.Sprintf("User %v: Error processing conjunct expression: %v", userId, err), http.StatusBadRequest) return } finalContentBuffer = finalContentBuffer.Union(tmpContentBuffer) } stats = finalContentBuffer.CalcEntityStats() } else { // Use the global stats, since this user has no filter set. logger.Printf("No entity filter provided, so using global entity stats.") finalContentBuffer = baseContentBuffer stats = finalContentBuffer.LatestEntityStats() } topEntities := map[string][]server.Entity{ "Person": annotateEntities(mgr.ContentDAO.PersonDAO, stats.TopPersons), "Org": annotateEntities(mgr.ContentDAO.OrgDAO, stats.TopOrgs), "Place": annotateEntities(mgr.ContentDAO.PlaceDAO, stats.TopPlaces), } entityTimes, entityValues := stats.EntityTrend.Data() latestNews := make([]server.NewsArticle, 0, len(stats.LatestNews)) for _, doc := range stats.LatestNews { newsArticle := server.NewsArticle{ Document: doc, Persons: annotateEntities(mgr.ContentDAO.PersonDAO, makeEntities(finalContentBuffer.PersonGraph.EntityIdsForDocument(doc.Id).Items())), Orgs: annotateEntities(mgr.ContentDAO.OrgDAO, makeEntities(finalContentBuffer.OrgGraph.EntityIdsForDocument(doc.Id).Items())), Places: annotateEntities(mgr.ContentDAO.PlaceDAO, makeEntities(finalContentBuffer.PlaceGraph.EntityIdsForDocument(doc.Id).Items())), } latestNews = append(latestNews, newsArticle) } response := map[string]interface{}{ "EntityTrend": EntityTrend{entityTimes, entityValues}, "TopEntities": topEntities, "LatestNews": latestNews, } sendJsonResponse(response, w) }) // End getHttpRequestBody() } }