diff --git a/VERSION b/VERSION index ece61c6..f9cbc01 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.6 \ No newline at end of file +1.0.7 \ No newline at end of file diff --git a/config.json b/config.json index 26f2cd4..f6d0f3e 100644 --- a/config.json +++ b/config.json @@ -3,7 +3,7 @@ "dbMySqlUser": "root", "dbMySqlPassword": "lepanos", "dbMySqlName": "asteriskcdrdb", - "dbMySqlFetchRowNumber": "100", + "dbMySqlFetchRowNumber": "25", "mongoHost": "127.0.0.1", "mongoDbName" : "revor", "asteriskID": "asterisk1", @@ -12,103 +12,56 @@ "asteriskUser" : "vormanager", "asteriskPassword": "lepanos", "testCallActive": false, - "testCallSchedule": 120, + "testCallSchedule": 3600, "eventsMongoHost": "127.0.0.1", "purgeCelEvents": false, "notifications": [ "f1com" ], "dialplanContext": [ - { "name": "app-out", "direction": 1}, - { "name": "DLPN_DialPlan1", "direction": 1}, - { "name": "app-asts-ecoute", "direction": 2}, - { "name": "DLPN_DialPlan2", "direction": 2}, - { "name": "default", "direction": 2}, - { "name": "ringroups-custom-1", "direction": 2}, - { "name": "ringroups-custom-2", "direction": 2}, - { "name": "ringroups-custom-3", "direction": 2}, - { "name": "app-critel-op", "direction": 1}, - { "name": "app-pickup", "direction": 3}, - { "name": "app-in", "direction": 2}, - { "name": "from-internal", "direction": 1}, - { "name": "from-pstn", "direction": 2}, - { "name": "DID_span_1", "direction": 2}, - { "name": "DID_span_2", "direction": 2}, - { "name": "DID_span_3", "direction": 2}, - { "name": "DID_span_4", "direction": 2}, - { "name": "DID_span_5", "direction": 2}, - { "name": "app-fax-in", "direction": 2}, - { "name": "app-asts-ecoute", "direction": 2}, - { "name": "voicemenu-custom-1", "direction": 2}, - { "name": "voicemenu-custom-2", "direction": 2}, - { "name": "voicemenu-custom-3", "direction": 2}, - { "name": "voicemenu-custom-4", "direction": 2}, - { "name": "voicemenu-custom-5", "direction": 2}, - { "name": "voicemenu-custom-6", "direction": 2}, - { "name": "voicemenu-custom-7", "direction": 2}, - { "name": "voicemenu-custom-8", "direction": 2}, - { "name": "voicemenu-custom-9", "direction": 2}, - { "name": "voicemenu-custom-10", "direction": 2}, - { "name": "voicemenu-custom-11", "direction": 2}, - { "name": "voicemenu-custom-12", "direction": 2}, - { "name": "voicemenu-custom-13", "direction": 2}, - { "name": "voicemenu-custom-14", "direction": 2}, - { "name": "voicemenu-custom-15", "direction": 2}, - { "name": "voicemenu-custom-16", "direction": 2}, - { "name": "voicemenu-custom-17", "direction": 2}, - { "name": "voicemenu-custom-18", "direction": 2}, - { "name": "voicemenu-custom-19", "direction": 2}, - { "name": "voicemenu-custom-20", "direction": 2}, - { "name": "voicemenu-custom-21", "direction": 2}, - { "name": "voicemenu-custom-22", "direction": 2}, - { "name": "voicemenu-custom-23", "direction": 2}, - { "name": "voicemenu-custom-24", "direction": 2}, - { "name": "voicemenu-custom-25", "direction": 2}, - { "name": "voicemenu-custom-26", "direction": 2}, - { "name": "voicemenu-custom-27", "direction": 2}, - { "name": "voicemenu-custom-28", "direction": 2}, - { "name": "voicemenu-custom-29", "direction": 2}, - { "name": "voicemenu-custom-30", "direction": 2}, - { "name": "voicemenu-custom-31", "direction": 2}, - { "name": "voicemenu-custom-32", "direction": 2}, - { "name": "voicemenu-custom-33", "direction": 2}, - { "name": "voicemenu-custom-34", "direction": 2}, - { "name": "voicemenu-custom-35", "direction": 2}, - { "name": "voicemenu-custom-36", "direction": 2}, - { "name": "voicemenu-custom-37", "direction": 2}, - { "name": "voicemenu-custom-38", "direction": 2}, - { "name": "voicemenu-custom-39", "direction": 2}, - { "name": "voicemenu-custom-40", "direction": 2}, - { "name": "voicemenu-custom-41", "direction": 2}, - { "name": "voicemenu-custom-42", "direction": 2}, - { "name": "voicemenu-custom-43", "direction": 2}, - { "name": "voicemenu-custom-44", "direction": 2}, - { "name": "voicemenu-custom-45", "direction": 2}, - { "name": "voicemenu-custom-46", "direction": 2}, - { "name": "voicemenu-custom-47", "direction": 2}, - { "name": "voicemenu-custom-48", "direction": 2}, - { "name": "voicemenu-custom-49", "direction": 2}, - { "name": "voicemenu-custom-50", "direction": 2}, - { "name": "voicemenu-custom-51", "direction": 2}, - { "name": "voicemenu-custom-52", "direction": 2}, - { "name": "voicemenu-custom-53", "direction": 2}, - { "name": "voicemenu-custom-54", "direction": 2}, - { "name": "voicemenu-custom-55", "direction": 2}, - { "name": "voicemenu-custom-56", "direction": 2}, - { "name": "voicemenu-custom-57", "direction": 2}, - { "name": "voicemenu-custom-58", "direction": 2}, - { "name": "voicemenu-custom-59", "direction": 2}, - { "name": "voicemenu-custom-60", "direction": 2}, - { "name": "voicemenu-custom-61", "direction": 2}, - { "name": "voicemenu-custom-62", "direction": 2}, - { "name": "voicemenu-custom-63", "direction": 2}, - { "name": "voicemenu-custom-64", "direction": 3}, - { "name": "voicemenu-custom-65", "direction": 2}, - { "name": "voicemenu-custom-66", "direction": 2}, - { "name": "voicemenu-custom-67", "direction": 2}, - { "name": "voicemenu-custom-68", "direction": 2}, - { "name": "asterisk_guitools", "direction":3} + { "name": "app-alive-test", "direction": 3}, + { "name": "app-custom-2587-day", "direction": 2}, + { "name": "app-custom-from-pstn", "direction": 2}, + { "name": "app-custom-6705", "direction": 2}, + { "name": "app-custom-6710", "direction": 2}, + { "name": "app-custom-6711", "direction": 2}, + { "name": "app-custom-blf-queue", "direction": 3}, + { "name": "stdexten-NOANSWER", "direction": 3}, + { "name": "app-custom-getvoicemail", "direction": 3}, + { "name": "app-custom-op-sugar", "direction": 2}, + { "name": "app-ecoute-pvsa-bry", "direction": 2}, + { "name": "app-custom-ecoute-from-ludres", "direction": 2}, + { "name": "app-iax2-bischheim", "direction": 1}, + { "name": "app-iax2-ludres", "direction": 1}, + { "name": "app-custom-ecoute-to-ludres", "direction": 2}, + { "name": "app-ecoute-pvsa-ludres", "direction": 2}, + { "name": "app-iax2-cofintex", "direction": 3}, + { "name": "app-out", "direction": 1}, + { "name": "app-out-hangup-call-party", "direction": 1}, + { "name": "app-pickup", "direction": 3}, + { "name": "asterisk_guitools", "direction": 3}, + { "name": "default", "direction": 3}, + { "name": "DID_span_1", "direction": 2}, + { "name": "DID_span_2", "direction": 2}, + { "name": "DID_span_3", "direction": 1}, + { "name": "DID_span_4", "direction": 1}, + { "name": "DLPN_DialPlan1", "direction": 1}, + { "name": "DLPN_DialPlan2", "direction": 2}, + { "name": "process-in-fax", "direction": 3}, + { "name": "process-in-Modem-RSI", "direction": 3}, + { "name": "process-in-rsi", "direction": 3}, + { "name": "process-in-smb", "direction": 3}, + { "name": "ringroups-custom-1", "direction": 2}, + { "name": "ringroups-custom-2", "direction": 2}, + { "name": "ringroups-custom-3", "direction": 2}, + { "name": "ringroups-custom-4", "direction": 2}, + { "name": "ringroups-custom-5", "direction": 2}, + { "name": "stdexten", "direction": 3}, + { "name": "voicemenu-custom-1", "direction": 3} - - ] + + ], + "excludeFromAnalytics": [ + "8889" + ] } diff --git a/docs/INSTALL.md b/docs/INSTALL.md index c88f319..bfa9c53 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -56,6 +56,10 @@ Mise à jour est identique à l'installation sans la partie de la configuration. Il faut copier le ficheir de la configuration actuel (/opt/vorimport/current/config.json) dans le repertoire /opt/vorimport/vorimport_[version] +Paramètre excludeFromAnalytics permet exclure des SDA de processus d'importation. Ce cas peut être intéressant car les numéro des SDA sont équivalant au numéro des postes. + +Importat : Importation de SDA est basée sur les SDA saisie dans la base de données. Ces SDA peut être saisie via l'interface graphique + ## Notes Cluster diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md index b93aab6..2788eb9 100644 --- a/docs/ReleaseNotes.md +++ b/docs/ReleaseNotes.md @@ -2,6 +2,22 @@ Release Notes vorimport ---------- +================================================================================== + +Release 1.0.7 date : 20/03/2015 + +================================================================================== + +Fixing: + + Fix DID import process + +New: + + Add new parameter excludeFromAnalytics. Allow exclude did from analysing process + + + ================================================================================== Release 1.0.6 date : 10/03/2015 diff --git a/main.go b/main.go index bd4ed81..1432796 100644 --- a/main.go +++ b/main.go @@ -47,6 +47,7 @@ type Config struct { DialplanContext []Context Notifications []string Dids []string + ExcludeFromAnalytics []string } var ( @@ -64,8 +65,9 @@ var ( ) const ( - DIRECTION_CALL_OUT = 1 - DIRECTION_CALL_IN = 2 + DIRECTION_CALL_OUT = 1 + DIRECTION_CALL_IN = 2 + DIRECTION_CALL_IGNORE = 3 ) var ( @@ -285,8 +287,7 @@ func importJob() { os.Exit(1) } // - log.Tracef("Get [%d] details records for the call with uniqueud [%s].", - len(callDetails), cdr.Uniqueid) + log.Tracef("Get [%d] details records for the call with uniqueid [%s].", len(callDetails), cdr.Uniqueid) if callDetails != nil { cdr.CallDetails = callDetails } @@ -307,16 +308,19 @@ func importJob() { var callDetail = cdr.CallDetails[i] if i == 0 && callDetail.EventType == "CHAN_START" { exten = callDetail.Exten + did = callDetail.Exten } if callDetail.EventType == "ANSWER" && exten == callDetail.CidDnid && did == "" { + //try to find did did = callDetail.CidDnid } else if callDetail.EventType == "BRIDGE_START" { //bridge start gives the time before answer cdr.AnswerWaitTime = int(callDetail.EventTime.Unix() - cdr.Calldate.Unix()) + peer = getPeerFromChannel(callDetail.Peer) - } else if callDetail.EventType == "BRIDGE_END" { + } else if callDetail.EventType == "BRIDGE_END" && peer == "" { //idea to find the last BRIDGE_END event and get the extention from it peer = getPeerFromChannel(callDetail.Peer) break @@ -327,6 +331,11 @@ func importJob() { peer = getPeerFromChannel(cdr.Dstchannel) } + if peer == "" { + //can be case that the call is not answered + peer = cdr.Dst + } + cdr.Peer = peer //there is a possibility to have for incomming call a peer for the did number @@ -340,7 +349,7 @@ func importJob() { cdr.Did = "" } - log.Tracef("Didi verification [%s] and cdtDid [%s].\n", did, cdr.Did) + log.Tracef("Did [%s] and peer [%s] for the call with uniqueid [%s].\n", did, peer, cdr.Uniqueid) if cdr.Dst == "s" && peer != "" { // @@ -353,7 +362,9 @@ func importJob() { err = importCdrToMongo(session, cdr) var importedStatus = 1 if err != nil { - importedStatus = -1 + log.Errorf("Can't import cdr to mongo [%v].", err) + log.Flush() + os.Exit(1) } // log.Debugf("Import executed for unique id [%s] with code : [%d], try process the mysql updating.\n", diff --git a/main_test.go b/main_test.go index 5043a64..3246509 100644 --- a/main_test.go +++ b/main_test.go @@ -7,6 +7,11 @@ import ( ) func getPeerFromChannel(channel string) (peer string) { + + if strings.Contains(channel, "IAX2/trunk_") { + return "" + } + //try to find the destination from channel delim := '-' if strings.Contains(channel, "@") { @@ -58,6 +63,13 @@ func Test_DstChannel(t *testing.T) { t.Fail() } + channel = "IAX2/trunk_2-13959" + peer = getPeerFromChannel(channel) + if peer != "" { + t.Errorf("It is not good peer [%s] for channel [%s].", peer, channel) + t.Fail() + } + t.Log("dstChannelTester test passed.") } diff --git a/mongo.go b/mongo.go index e9e4f25..6b79c75 100644 --- a/mongo.go +++ b/mongo.go @@ -28,6 +28,18 @@ func importCdrToMongo(session *mgo.Session, cdr RawCall) (err error) { log.Debugf("Start analyze data for mongo database from asterisk : [%s].", cdr.AsteriskId) createMongoCdr(session, cdr) + for i := range config.ExcludeFromAnalytics { + if config.ExcludeFromAnalytics[i] == cdr.Peer || config.ExcludeFromAnalytics[i] == cdr.Did { + log.Debugf("Call with the uniqueid skipped [%s] from the analytic process.\n ", cdr.Uniqueid) + return nil + + } + } + + if cdr.InoutStatus == DIRECTION_CALL_IGNORE { + return nil + } + if cdr.Dst != "s" { //can import for peer(users) err = processDailyAnalytics(session, cdr) @@ -49,6 +61,6 @@ func importCdrToMongo(session *mgo.Session, cdr RawCall) (err error) { if cdr.InoutStatus == DIRECTION_CALL_IN { err = processDidImport(session, cdr) } - err = nil - return + + return err } diff --git a/mongopeer.go b/mongopeer.go index b693f4d..6189dca 100644 --- a/mongopeer.go +++ b/mongopeer.go @@ -16,13 +16,25 @@ func processPeerMonthlySummary(session *mgo.Session, cdr RawCall) (err error) { var peer = "" if cdr.InoutStatus == DIRECTION_CALL_OUT { collectionName = "monthlypeeroutgoing_summary" - peer = cdr.Src + if len(cdr.Src) == 4 { + peer = cdr.Src + } else { + peer = cdr.Dst + } + } else if cdr.InoutStatus == DIRECTION_CALL_IN { collectionName = "monthlypeerincomming_summary" peer = cdr.Peer + } else { return errors.New("Can't detect the call context") } + + if len(peer) > 5 { + serr := fmt.Sprintf("Peer monthly summary something is wrong with the call [%s].Please verify your log.\n", cdr.Uniqueid) + return errors.New(serr) + } + // var id = fmt.Sprintf("%04d%02d-%s", cdr.Calldate.Year(), cdr.Calldate.Month(), peer) @@ -77,19 +89,36 @@ func processPeerMonthlySummary(session *mgo.Session, cdr RawCall) (err error) { func processMonthlyAnalytics(session *mgo.Session, cdr RawCall) (err error) { // var collectionName = "" - var dst = "" + var peer = "" if cdr.InoutStatus == DIRECTION_CALL_OUT { collectionName = "monthlypeer_outgoing" - dst = cdr.Src + if len(cdr.Src) == 4 { + peer = cdr.Src + } else { + peer = cdr.Dst + } } else if cdr.InoutStatus == DIRECTION_CALL_IN { collectionName = "monthlypeer_incomming" - dst = cdr.Peer + peer = cdr.Peer } else { return errors.New("Can't detect the call context") } + + if len(peer) > 5 { + serr := fmt.Sprintf("Monthly analytics something is wrong with the call [%s].Please verify your log.\n", cdr.Uniqueid) + return errors.New(serr) + } + + if peer == "" { + var serrr = fmt.Sprintf("Can't get a valide destination for the call with the uniqueid [%s].\n", cdr.Uniqueid) + log.Error(serrr) + log.Flush() + return errors.New(serrr) + } + // var id = fmt.Sprintf("%04d%02d-%s-%d", cdr.Calldate.Year(), - cdr.Calldate.Month(), dst, cdr.Disposition) + cdr.Calldate.Month(), peer, cdr.Disposition) // var metaDate = time.Date(cdr.Calldate.Year(), cdr.Calldate.Month(), 1, 1, 0, 0, 0, time.UTC) @@ -97,7 +126,7 @@ func processMonthlyAnalytics(session *mgo.Session, cdr RawCall) (err error) { var collection = session.DB(config.MongoDbName).C(collectionName) - metaDoc := MetaData{Dst: dst, Dt: metaDate, Disposition: cdr.Disposition} + metaDoc := MetaData{Dst: peer, Dt: metaDate, Disposition: cdr.Disposition} doc := MonthlyCall{Id: id, Meta: metaDoc, AnswereWaitTime: 0, CallMonthly: 0, DurationMonthly: 0} @@ -143,25 +172,37 @@ func processMonthlyAnalytics(session *mgo.Session, cdr RawCall) (err error) { func processDailyAnalytics(session *mgo.Session, cdr RawCall) (err error) { // var collectionName = "" - var dst = "" + var peer = "" if cdr.InoutStatus == DIRECTION_CALL_OUT { collectionName = "dailypeer_outgoing" - dst = cdr.Src + if len(cdr.Src) == 4 { + peer = cdr.Src + } else { + peer = cdr.Dst + } + } else if cdr.InoutStatus == DIRECTION_CALL_IN { collectionName = "dailypeer_incomming" - dst = cdr.Peer + peer = cdr.Peer + } else { return errors.New("Daily analytics can't detect the call context") } + + if len(peer) > 5 { + serr := fmt.Sprintf("Daily analytics something is wrong with the call [%s] cause peer [%s].Please verify your log.\n", cdr.Uniqueid, peer) + return errors.New(serr) + } + // var id = fmt.Sprintf("%04d%02d%02d-%s-%d", cdr.Calldate.Year(), cdr.Calldate.Month(), - cdr.Calldate.Day(), dst, cdr.Disposition) + cdr.Calldate.Day(), peer, cdr.Disposition) var metaDate = time.Date(cdr.Calldate.Year(), cdr.Calldate.Month(), cdr.Calldate.Day(), 1, 0, 0, 0, time.UTC) var collection = session.DB(config.MongoDbName).C(collectionName) - metaDoc := MetaData{Dst: dst, Dt: metaDate, Disposition: cdr.Disposition} + metaDoc := MetaData{Dst: peer, Dt: metaDate, Disposition: cdr.Disposition} doc := DailyCall{Id: id, Meta: metaDoc, AnswereWaitTime: 0, CallDaily: 0, DurationDaily: 0} diff --git a/mysql.go b/mysql.go index c5898b9..ee8433b 100644 --- a/mysql.go +++ b/mysql.go @@ -224,35 +224,42 @@ func getMySqlCallDetails(db mysql.Conn, uniqueid string) (results []CallDetail, return nil, nil } // - var searchIdMap = make(map[string]string) - for _, row := range rows { - uniqueid := res.Map("uniqueid") - linkedid := res.Map("linkedid") - keyUniqueid := fmt.Sprintf("'%s'", row.Str(uniqueid)) - keyLinkedId := fmt.Sprintf("'%s'", row.Str(linkedid)) - searchIdMap[keyUniqueid] = keyUniqueid - searchIdMap[keyLinkedId] = keyLinkedId - - } - var keys []string - for k := range searchIdMap { - keys = append(keys, k) - } - var strIds = strings.Join(keys, ",") - - var sqlNext = sqlBase + "uniqueid IN (" + strIds + ") OR linkedid IN (" + strIds + ")" + sqlOrder - // - rowsNext, resNext, errNext := db.Query(sqlNext) - if errNext != nil { - log.Errorf(" getMySqlCallDetailsExecuting request [%s] and get error [%s] \r\n", sqlNext, errNext) - return nil, errNext - } - - if len(rowsNext) == 0 { - return nil, nil - } + //var searchIdMap = make(map[string]string) + //for _, row := range rows { + // uniqueid := res.Map("uniqueid") + // linkedid := res.Map("linkedid") + // keyUniqueid := fmt.Sprintf("'%s'", row.Str(uniqueid)) + // keyLinkedId := fmt.Sprintf("'%s'", row.Str(linkedid)) + // searchIdMap[keyUniqueid] = keyUniqueid + // searchIdMap[keyLinkedId] = keyLinkedId + + //} + //var keys []string + //for k := range searchIdMap { + // keys = append(keys, k) + //} + //var strIds = strings.Join(keys, ",") + + //var sqlNext = sqlBase + "uniqueid IN (" + strIds + ") OR linkedid IN (" + strIds + ")" + sqlOrder + //// + + //log.Tracef("Call details query : [%s]", sqlNext) + + //rowsNext, resNext, errNext := db.Query(sqlNext) + //if errNext != nil { + // log.Errorf(" getMySqlCallDetailsExecuting request [%s] and get error [%s] \r\n", sqlNext, errNext) + // return nil, errNext + //} + + //if len(rowsNext) == 0 { + // return nil, nil + //} + + rowsNext := rows + resNext := res + + ////prepare results array - //prepare results array results = make([]CallDetail, len(rowsNext)) i := 0 diff --git a/samples/config.sample.json b/samples/config.sample.json index a63e1f7..d4eb229 100644 --- a/samples/config.sample.json +++ b/samples/config.sample.json @@ -22,7 +22,13 @@ { "name": "DLPN_DialPlan1", "direction": 2}, { "name": "ringroups-custom-1", "direction": 2}, { "name": "DID_span_1", "direction": 2}, - { "name": "app-daynight-toggle", "direction": 3} - ] + { "name": "app-daynight-toggle", "direction": 3}, + { "name": "app-pickup", "direction": 3}, + { "name": "asterisk_guitools", "direction": 3}, + { "name": "default", "direction": 3} + ], + "excludeFromAnalytics": [ + + ] } diff --git a/tools.go b/tools.go index 4f6032d..e6f2138 100644 --- a/tools.go +++ b/tools.go @@ -6,6 +6,10 @@ import ( ) func getPeerFromChannel(channel string) (peer string) { + if strings.Contains(channel, "IAX2/trunk_") { + return "" + } + //try to find the destination from channel delim := '-' if strings.Contains(channel, "@") {