fork download
  1. package main
  2.  
  3. // TopCoder MM DataFeeds
  4. // community.topcoder.com/longcontest/?module=Static&d1=support&d2=dataFeed
  5. // TopCoder API
  6. // tcapi.docs.apiary.io/
  7.  
  8. import (
  9. "bufio"
  10. "encoding/json"
  11. "encoding/xml"
  12. "fmt"
  13. "io"
  14. "io/ioutil"
  15. "log"
  16. "math"
  17. "net/http"
  18. "os"
  19. "regexp"
  20. "sort"
  21. "strconv"
  22. "strings"
  23. "time"
  24. )
  25.  
  26. const (
  27. Protocol = "http"
  28. Server = "www.topcoder.com"
  29. RoundListPath = "/tc?module=BasicData&c=dd_marathon_round_list"
  30. RoundResultsPath = "/tc?module=BasicData&c=dd_marathon_round_results&rd=%d"
  31. IndividualResultsPath = "/longcontest/stats/?module=IndividualResultsFeed&rd=%d&cr=%d"
  32. ApiServer = "api.topcoder.com"
  33. PublicProfileApi = "/v2/users/%s"
  34. )
  35.  
  36. func MakeRoundListUrl() string {
  37. return Protocol + "://" + Server + RoundListPath
  38. }
  39.  
  40. func MakeRoundResultsUrl(rd int64) string {
  41. return Protocol + "://" + Server + fmt.Sprintf(RoundResultsPath, rd)
  42. }
  43.  
  44. func MakeIndividualResultsUrl(rd, cr int64) string {
  45. return Protocol + "://" + Server + fmt.Sprintf(IndividualResultsPath, rd, cr)
  46. }
  47.  
  48. func MakePublicProfileApiUrl(handle string) string {
  49. return Protocol + "://" + ApiServer + fmt.Sprintf(PublicProfileApi, handle)
  50. }
  51.  
  52. type Round struct {
  53. RoundId int64 `xml:"round_id"`
  54. FullName string `xml:"full_name"`
  55. ShortName string `xml:"short_name"`
  56. Date string `xml:"date"`
  57. RoundType string `xml:"round_type"`
  58. }
  59.  
  60. type RoundList struct {
  61. XMLName xml.Name `xml:"dd_marathon_round_list"`
  62. Rounds []*Round `xml:"row"`
  63. }
  64.  
  65. type Result struct {
  66. RoundId int64 `xml:"round_id"`
  67. CoderId int64 `xml:"coder_id"`
  68. Handle string `xml:"handle"`
  69. OldRating int `xml:"old_rating"`
  70. NewRating int `xml:"new_rating"`
  71. OldVolatility int `xml:"old_volatility"`
  72. NewVolatility int `xml:"new_volatility"`
  73. NumRatings int `xml:"num_ratings"`
  74. Placed int `xml:"placed"`
  75. Advanced string `xml:"advanced"`
  76. ProvisionalScore float64 `xml:"provisional_score"`
  77. FinalScore float64 `xml:"final_score"`
  78. NumSubmissions int `xml:"num_submissions"`
  79. RatedFlag int `xml:"rated_flag"`
  80. }
  81.  
  82. type RoundResults struct {
  83. XMLName xml.Name `xml:"dd_marathon_round_results"`
  84. Results []*Result `xml:"row"`
  85. }
  86.  
  87. type Submission struct {
  88. Number int `xml:"number"`
  89. Score float64 `xml:"score"`
  90. Language string `xml:"language"`
  91. Time string `xml:"time"`
  92. }
  93.  
  94. type TestCase struct {
  95. TestCaseId int64 `xml:"test_case_id"`
  96. Score float64 `xml:"score"`
  97. ProcessingTime int `xml:"processing_time"`
  98. FatalErrorInd int `xml:"fatal_error_ind"`
  99. }
  100.  
  101. type IndividualResults struct {
  102. XMLName xml.Name `xml:"marathon_individual_results"`
  103. RoundId int64 `xml:"round_id"`
  104. CoderId int64 `xml:"coder_id"`
  105. Handle string `xml:"handle"`
  106. Submissions []*Submission `xml:"submissions>submission"`
  107. TestCases []*TestCase `xml:"testcases>testcase"`
  108. }
  109.  
  110. type PublicProfile map[string]interface{}
  111.  
  112. type Detail struct {
  113. PublicProfile
  114. *IndividualResults
  115. ProvisionalPlaced, AverageTime, Bests, Uniques, Zeros int
  116. }
  117.  
  118. func (rr RoundResults) ProvisionalStandings() []int {
  119. standings := make([]int, len(rr.Results))
  120. indexes := make([]int, len(rr.Results))
  121. for i := range indexes {
  122. indexes[i] = i
  123. }
  124. sort.Slice(indexes, func(i, j int) bool {
  125. return rr.Results[indexes[i]].ProvisionalScore > rr.Results[indexes[j]].ProvisionalScore
  126. })
  127. place := 1
  128. for p, i := range indexes {
  129. if p > 0 && rr.Results[indexes[p-1]].ProvisionalScore > rr.Results[i].ProvisionalScore {
  130. place = p + 1
  131. }
  132. standings[i] = place
  133. }
  134. return standings
  135. }
  136.  
  137. func (rr RoundResults) Details() ([]*Detail, error) {
  138. details := make([]*Detail, len(rr.Results))
  139. pstandings := rr.ProvisionalStandings()
  140. for i, r := range rr.Results {
  141. log.Println(i+1, "/", len(details))
  142. time.Sleep(5 * time.Second)
  143. pf, err := GetPublicProfile(r.Handle)
  144. if err != nil {
  145. return nil, err
  146. }
  147. time.Sleep(5 * time.Second)
  148. ir, err := GetIndividualResults(r.RoundId, r.CoderId)
  149. if err != nil {
  150. return nil, err
  151. }
  152. at := ir.AverageTime()
  153. zr := ir.Zeros()
  154. details[i] = &Detail{pf, ir, pstandings[i], at, 0, 0, zr}
  155. }
  156. var update func(a, b float64) bool
  157. testCaseCount := len(details[0].TestCases)
  158. e := len(details) / 2
  159. less := 0
  160. for i := 0; i < testCaseCount; i++ {
  161. if details[0].TestCases[i].Score < details[e].TestCases[i].Score {
  162. less++
  163. }
  164. }
  165. bestScores := make([]float64, testCaseCount)
  166. bestCounts := make([]int, testCaseCount)
  167. if less > testCaseCount-less {
  168. update = func(a, b float64) bool { return a > 0.0 && a < b }
  169. for i := range bestScores {
  170. bestScores[i] = math.MaxFloat64
  171. }
  172. } else {
  173. update = func(a, b float64) bool { return a > b }
  174. }
  175. for _, dt := range details {
  176. for i, tc := range dt.TestCases {
  177. if update(tc.Score, bestScores[i]) {
  178. bestScores[i] = tc.Score
  179. bestCounts[i] = 1
  180. } else if tc.Score == bestScores[i] {
  181. bestCounts[i]++
  182. }
  183. }
  184. }
  185. for _, dt := range details {
  186. for i, tc := range dt.TestCases {
  187. if tc.Score == bestScores[i] {
  188. dt.Bests++
  189. if bestCounts[i] == 1 {
  190. dt.Uniques++
  191. }
  192. }
  193. }
  194. }
  195. return details, nil
  196. }
  197.  
  198. func (ir *IndividualResults) AverageTime() int {
  199. if ir == nil {
  200. return 0
  201. }
  202. sum := 0
  203. count := 0
  204. for _, tc := range ir.TestCases {
  205. if tc.ProcessingTime > 0 {
  206. sum += tc.ProcessingTime
  207. count++
  208. }
  209. }
  210. if count > 0 {
  211. return (sum + count - 1) / count
  212. } else {
  213. return 0
  214. }
  215. }
  216.  
  217. func (ir *IndividualResults) Zeros() int {
  218. if ir == nil {
  219. return 0
  220. }
  221. count := 0
  222. for _, tc := range ir.TestCases {
  223. if tc.Score == 0.0 || tc.Score < 0.0 {
  224. count++
  225. }
  226. }
  227. return count
  228. }
  229.  
  230. func (p PublicProfile) Country() string {
  231. if p == nil {
  232. return ""
  233. }
  234. if value, ok := p["country"]; ok {
  235. switch country := value.(type) {
  236. case string:
  237. return country
  238. default:
  239. return ""
  240. }
  241. } else {
  242. return ""
  243. }
  244. }
  245.  
  246. func CharsetReader(charset string, input io.Reader) (io.Reader, error) {
  247. // change charset to UTF-8
  248. // no implements (almost, datafeeds have only ascii chars)
  249. return input, nil
  250. }
  251.  
  252. func GetDataFeed(url string, v interface{}) error {
  253. resp, err := http.Get(url)
  254. if err != nil {
  255. return err
  256. }
  257. defer resp.Body.Close()
  258. decoder := xml.NewDecoder(resp.Body)
  259. decoder.CharsetReader = CharsetReader
  260. err = decoder.Decode(v)
  261. if err != nil {
  262. return err
  263. }
  264. return nil
  265. }
  266.  
  267. func GetRoundList() (*RoundList, error) {
  268. log.Println("download RoundList")
  269. url := MakeRoundListUrl()
  270. v := &RoundList{}
  271. if err := GetDataFeed(url, v); err != nil {
  272. return nil, err
  273. }
  274. sort.Slice(v.Rounds, func(i, j int) bool {
  275. return v.Rounds[i].Date > v.Rounds[j].Date
  276. })
  277. return v, nil
  278. }
  279.  
  280. func GetRoundResults(rd int64) (*RoundResults, error) {
  281. log.Println("download RoundResults", rd)
  282. url := MakeRoundResultsUrl(rd)
  283. v := &RoundResults{}
  284. if err := GetDataFeed(url, v); err != nil {
  285. return nil, err
  286. }
  287. sort.Slice(v.Results, func(i, j int) bool {
  288. return v.Results[i].Placed < v.Results[j].Placed
  289. })
  290. return v, nil
  291. }
  292.  
  293. func GetIndividualResults(rd, cr int64) (*IndividualResults, error) {
  294. log.Println("download IndividualResults", rd, cr)
  295. url := MakeIndividualResultsUrl(rd, cr)
  296. v := &IndividualResults{}
  297. if err := GetDataFeed(url, v); err != nil {
  298. return nil, err
  299. }
  300. sort.Slice(v.Submissions, func(i, j int) bool {
  301. return v.Submissions[i].Number < v.Submissions[j].Number
  302. })
  303. sort.Slice(v.TestCases, func(i, j int) bool {
  304. return v.TestCases[i].TestCaseId < v.TestCases[j].TestCaseId
  305. })
  306. return v, nil
  307. }
  308.  
  309. func GetPublicProfile(handle string) (PublicProfile, error) {
  310. log.Println("download PubicProfile", handle)
  311. url := MakePublicProfileApiUrl(handle)
  312. resp, err := http.Get(url)
  313. if err != nil {
  314. return nil, err
  315. }
  316. defer resp.Body.Close()
  317. data, err := ioutil.ReadAll(resp.Body)
  318. if err != nil {
  319. return nil, err
  320. }
  321. var f interface{}
  322. err = json.Unmarshal(data, &f)
  323. if err != nil {
  324. return nil, err
  325. }
  326. return PublicProfile(f.(map[string]interface{})), nil
  327. }
  328.  
  329. func FilterByRegexp(dst *[]*Round, src []*Round, expr string) error {
  330. list := (*dst)[:0]
  331. if len(expr) == 0 {
  332. return nil
  333. }
  334. re, err := regexp.Compile(expr)
  335. if err != nil {
  336. return err
  337. }
  338. for _, r := range src {
  339. if re.MatchString(r.ShortName) || re.MatchString(r.FullName) {
  340. list = append(list, r)
  341. }
  342. }
  343. (*dst) = list
  344. return nil
  345. }
  346.  
  347. func FilterBySubstr(dst *[]*Round, src []*Round, substr string) {
  348. list := (*dst)[:0]
  349. if len(substr) == 0 {
  350. return
  351. }
  352. for _, r := range src {
  353. if strings.Contains(r.ShortName, substr) || strings.Contains(r.FullName, substr) {
  354. list = append(list, r)
  355. }
  356. }
  357. (*dst) = list
  358. }
  359.  
  360. func SelectRound() (*Round, error) {
  361. roundList, err := GetRoundList()
  362. if err != nil {
  363. return nil, err
  364. }
  365. list := []*Round{}
  366. list = append(list, roundList.Rounds[:15]...)
  367. index := -1
  368. scanner := bufio.NewScanner(os.Stdin)
  369. showFullName := false
  370. for index < 0 {
  371. for i, r := range list {
  372. if showFullName {
  373. fmt.Println(fmt.Sprintf("%6d: %s", i+1, r.FullName))
  374. } else {
  375. fmt.Println(fmt.Sprintf("%6d: %s", i+1, r.ShortName))
  376. }
  377. }
  378. fmt.Print("index or command: ")
  379. scanner.Scan()
  380. input := scanner.Text()
  381. if strings.HasPrefix(input, "r:") {
  382. expr := strings.TrimPrefix(input, "r:")
  383. err = FilterByRegexp(&list, roundList.Rounds, expr)
  384. if err != nil {
  385. log.Println(err.Error())
  386. }
  387. } else if strings.HasPrefix(input, "s:") {
  388. substr := strings.TrimPrefix(input, "s:")
  389. FilterBySubstr(&list, roundList.Rounds, substr)
  390. } else if input == "exit" {
  391. return nil, nil
  392. } else if input == "full" {
  393. showFullName = true
  394. } else if input == "short" {
  395. showFullName = false
  396. } else {
  397. temp, err := strconv.Atoi(input)
  398. if err != nil {
  399. log.Println(err.Error())
  400. } else if temp < 1 || len(list) < temp {
  401. log.Println("invalid index: ", temp)
  402. } else {
  403. index = temp
  404. }
  405. }
  406. }
  407. return roundList.Rounds[index-1], nil
  408. }
  409.  
  410. func main() {
  411. round, err := SelectRound()
  412. if err != nil {
  413. log.Panic(err)
  414. }
  415. if round == nil {
  416. log.Println("canceled")
  417. return
  418. }
  419. filename := "statistics.txt"
  420. file, err := os.Create(filename)
  421. if err != nil {
  422. log.Panic(err)
  423. }
  424. defer file.Close()
  425. fmt.Println("Contest: ", round.FullName)
  426. fmt.Println("RoundId: ", round.RoundId)
  427. fmt.Println("Date: ", round.Date)
  428. fmt.Fprintln(file, "Contest: ", round.FullName)
  429. fmt.Fprintln(file, "RoundId: ", round.RoundId)
  430. fmt.Fprintln(file, "Date: ", round.Date)
  431. fmt.Fprintln(file, "Statistics")
  432. fmt.Fprintln(file, `<pre><font size="3">`)
  433. results, err := GetRoundResults(round.RoundId)
  434. if err != nil {
  435. log.Panic(err)
  436. }
  437. details, err := results.Details()
  438. if err != nil {
  439. log.Panic(err)
  440. }
  441. fmt.Fprintln(file, fmt.Sprintf(
  442. "<b>%-3s %-24s %-15s %-9s %-3s %-9s %-4s %-4s %-4s %-4s &#9650;&#9660;</b>",
  443. "POS", "HANDLE", "COUNTRY", "SCORE", "PRP", "PRSCORE",
  444. "BEST", "UNIQ", "ZERO", "TIME",
  445. ))
  446. changes := func(a, b int) string {
  447. if a < b {
  448. return fmt.Sprintf(`<font color="green">&#9650;%d</font>`, b-a)
  449. } else if a > b {
  450. return fmt.Sprintf(`<font color="red">&#9660;%d</font>`, a-b)
  451. } else {
  452. return "&#9642;0"
  453. }
  454. }
  455. for i, r := range results.Results {
  456. dt := details[i]
  457. pf := dt.PublicProfile
  458. fmt.Fprintln(file, fmt.Sprintf(
  459. "%3d %-27s %-15s %9.2f %3d %9.2f %4d %4d %4d %4d %s",
  460. r.Placed, "[h]"+r.Handle+"[/h]", pf.Country(), r.FinalScore,
  461. dt.ProvisionalPlaced, r.ProvisionalScore,
  462. dt.Bests, dt.Uniques, dt.Zeros, dt.AverageTime,
  463. changes(r.Placed, dt.ProvisionalPlaced),
  464. ))
  465. }
  466. fmt.Fprintln(file, "</font></pre>")
  467. log.Println("saved", filename)
  468. }
  469.  
Compilation error #stdin compilation error #stdout 0s 0KB
stdin
Standard input is empty
compilation info
# _/home/rTA42M
./prog.go:124: undefined: sort.Slice
./prog.go:274: undefined: sort.Slice
./prog.go:287: undefined: sort.Slice
./prog.go:300: undefined: sort.Slice
./prog.go:303: undefined: sort.Slice
stdout
Standard output is empty