-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#3 Added functionality for national election
Added functionality for national election
- Loading branch information
Showing
13 changed files
with
321 additions
and
106 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,11 @@ | ||
builds: | ||
- main: . | ||
binary: npelection | ||
id: npelection | ||
id: npelection | ||
|
||
goos: [darwin,linux,windows] | ||
goos: [darwin, linux, windows] | ||
goarch: | ||
- "386" | ||
- amd64 | ||
- arm | ||
- arm64 | ||
- arm64 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
default: release | ||
|
||
release: | ||
GOOS=linux GOARCH=amd64 go build -o ./build/linux-amd64/npelection ./main.go | ||
GOOS=linux GOARCH=arm go build -o ./build/linux-arm/npelection ./main.go | ||
GOOS=windows GOARCH=amd64 go build -o ./build/windows-amd64/npelection.exe ./main.go | ||
GOOS=windows GOARCH=arm64 go build -o ./build/windows-386/npelection.exe ./main.go | ||
GOOS=darwin GOARCH=amd64 go build -o ./build/darwin-amd64/npelection ./main.go | ||
|
||
lint: | ||
golangci-lint run | ||
go mod tidy -v && git --no-pager diff --quiet go.mod | ||
|
||
clean: | ||
rm -rf build | ||
|
||
install-linter: | ||
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest | ||
|
||
install-fumpt: | ||
go install mvdan.cc/gofumpt@latest | ||
|
||
install-tools: install-linter install-fumpt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,6 @@ NpElection displays the election data of Nepal right from the comfort of your te | |
|
||
- Download the latest release as per your system from the [Releases](https://github.com/askbuddie/npelection/releases) page. | ||
|
||
|
||
# How to use | ||
|
||
Once you have downloaded a release file, extract the zip and run from the relative directory: | ||
|
@@ -15,7 +14,6 @@ Once you have downloaded a release file, extract the zip and run from the relati | |
./npelection | ||
``` | ||
|
||
|
||
## Add to Path | ||
|
||
```bash | ||
|
@@ -24,24 +22,31 @@ Once you have downloaded a release file, extract the zip and run from the relati | |
|
||
Now, you will be able to use **`npelection`** as a command. | ||
|
||
|
||
## List candidates | ||
|
||
```bash | ||
./npelection list | ||
``` | ||
|
||
> **Note**, you can simply use: **`npelection list`** after doing **`./npelection init`** as mentioned earlier. | ||
List all districts. | ||
|
||
## Get Election Result for District | ||
|
||
```bash | ||
./npelection get -d <district> | ||
``` | ||
|
||
> **Note**, you can simply use: **`npelection get -d <district>`** after doing **`./npelection init`**. | ||
# Prerequisites (Build only) | ||
|
||
- [Go](https://golang.org/doc/install) | ||
|
||
|
||
# Build | ||
|
||
- Clone the repository with `git clone [email protected]:askbuddie/npelection.git` | ||
- Download the dependencies with `go mod download` | ||
- Run with `go run main.go` or `go build` | ||
- Run `sh build.sh` to build for all platforms (Windows, Linux, MacOS) | ||
- Run `make release` to build for all platforms (Windows, Linux, MacOS) | ||
|
||
**Disclaimer: This tool is made entirely for educational purposes. Any legal responsibility belongs to the person or organization that uses it.** |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package app | ||
|
||
import ( | ||
levenshtein "github.com/ka-weihe/fast-levenshtein" | ||
) | ||
|
||
func GetClosestDistrict(targetDistrict string, districtMap map[string]string) string { | ||
closestDistrict, closestDistance := "", 100 | ||
for district := range districtMap { | ||
distance := levenshtein.Distance(targetDistrict, district) | ||
if distance < closestDistance { | ||
closestDistance, closestDistrict = distance, district | ||
} | ||
} | ||
return closestDistrict | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
/* | ||
Copyright © 2022 NAME HERE <EMAIL ADDRESS> | ||
*/ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"net/http" | ||
"npelection/app" | ||
"os" | ||
"strings" | ||
|
||
"github.com/PuerkitoBio/goquery" | ||
"github.com/olekukonko/tablewriter" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var ( | ||
district string | ||
numParties int | ||
numCandidates int | ||
) | ||
|
||
// getCmd represents the get command | ||
var getCmd = &cobra.Command{ | ||
Use: "get", | ||
Short: "Get election data for certain district", | ||
RunE: func(cmd *cobra.Command, _ []string) error { | ||
// Get the link for the district to scrape | ||
link, ok := districtLinkMap[district] | ||
// If the district is not found, return an error | ||
if !ok { | ||
closestDistrict := app.GetClosestDistrict(district, districtLinkMap) | ||
fmt.Printf("Incorrect District Name. Did you mean %s?\n", closestDistrict) | ||
fmt.Printf("Use `npelection list` to get a list of all districts.\n\n") | ||
cmd.Help() | ||
return nil | ||
} | ||
// Get the HTML document | ||
response, err := http.Get(link) | ||
if err != nil { | ||
e := fmt.Errorf("error getting response: %v", err) | ||
return e | ||
} | ||
defer response.Body.Close() | ||
|
||
if response.StatusCode != 200 { | ||
e := fmt.Errorf("status code error- Got: %v Wanted: %v", response.StatusCode, http.StatusOK) | ||
return e | ||
} | ||
// Load the HTML document | ||
doc, err := goquery.NewDocumentFromReader(response.Body) | ||
if err != nil { | ||
e := fmt.Errorf("error parsing response body: %v", err) | ||
return e | ||
} | ||
|
||
// Find the table with constituency wise data | ||
constituencyTableHTML := doc.Find("body > section > section:nth-child(5) > div > div > div.gy-4 > div.election-2079 > div > div > div") | ||
constituencyTable := tablewriter.NewWriter(os.Stdout) | ||
constituencyTable.SetHeader([]string{"Constituency", "Candidate", "Party", "Vote", "Won"}) | ||
constituencyTable.SetAutoMergeCellsByColumnIndex([]int{0}) | ||
constituencyTable.SetAlignment(tablewriter.ALIGN_LEFT) | ||
constituencyTable.SetRowLine(true) | ||
|
||
// For each constituency, scrape the candidate name, party name, vote count and if their win condition. | ||
constituencyTableHTML.Each(func(_ int, constituencySelection *goquery.Selection) { | ||
constituencyName := constituencySelection.Find(".card-header > .card-title").Text() | ||
candidatesListHTML := constituencySelection.Find(".candidate-list__item") | ||
candidatesListHTML.EachWithBreak(func(i int, candidateSelection *goquery.Selection) bool { | ||
if i >= numCandidates { | ||
return false | ||
} | ||
candidateName := candidateSelection.Find(".nominee-name > a").Text() | ||
candidatePartyName := candidateSelection.Find(".candidate-party-name > a").Text() | ||
voteCount := candidateSelection.Find(".vote-count").Text() | ||
won := "" | ||
if candidateSelection.HasClass("elected") { | ||
won = "X" | ||
} | ||
constituencyTable.Append([]string{ | ||
strings.TrimSpace(constituencyName), | ||
strings.TrimSpace(candidateName), | ||
strings.TrimSpace(candidatePartyName), | ||
strings.TrimSpace(voteCount), | ||
won, | ||
}) | ||
return true | ||
}) | ||
}) | ||
constituencyTable.Render() | ||
|
||
// Find the table with overall party wise data | ||
partyTableHTML := doc.Find("body > section > section:nth-child(5) > div > div > div.row > div.col-xl-8 > div > div.col-xl-6.parties.mb-xl-0.mb-4 > div") | ||
partiesRow := partyTableHTML.Find(".card-body > .row") | ||
// Prepare table output in display | ||
partyTable := tablewriter.NewWriter(os.Stdout) | ||
partyTable.SetHeader([]string{"Party", "Win", "Lead"}) | ||
partyTable.SetRowLine(true) | ||
// For each party scrape the partyName, wins and leads in the given district | ||
partiesRow.EachWithBreak(func(i int, partySelection *goquery.Selection) bool { | ||
if i >= numParties { | ||
return false | ||
} | ||
partyName := partySelection.Find(".party-name > a").Text() | ||
numbers := partySelection.Find(".number-display") | ||
if numbers.Length() != 2 { | ||
log.Fatal(fmt.Errorf("unexpected html for wins and leads: %v", numbers.Nodes)) | ||
return false | ||
} | ||
wins := numbers.First().Text() | ||
leads := numbers.Last().Text() | ||
partyTable.Append([]string{ | ||
strings.TrimSpace(partyName), | ||
strings.TrimSpace(wins), | ||
strings.TrimSpace(leads), | ||
}) | ||
return true | ||
}) | ||
partyTable.Render() | ||
|
||
return nil | ||
}, | ||
} | ||
|
||
func init() { | ||
rootCmd.AddCommand(getCmd) | ||
|
||
getCmd.Flags().StringVarP(&district, "district", "d", "", "District for which the election data is to be shown") | ||
getCmd.Flags().IntVarP(&numParties, "parties", "p", 3, "Number of parties to show in the overall result") | ||
getCmd.Flags().IntVarP(&numCandidates, "candidates", "c", 3, "Number of candidates to show for each constituency") | ||
getCmd.MarkFlagRequired("district") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.