├── .gitignore
├── sample_nic_barcode.png
├── go.mod
├── main.go
├── nic_validator.go
├── README.md
├── go.sum
└── nic_generator.go
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | sri-lanka-nic-generator-validator
--------------------------------------------------------------------------------
/sample_nic_barcode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/randikabanura/sri_lanka_nic_generator_validator/HEAD/sample_nic_barcode.png
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module randikabanura/sri-lanka-nic-generator-validator
2 |
3 | go 1.16
4 |
5 | require (
6 | github.com/Pallinder/go-randomdata v1.2.0
7 | github.com/boombuler/barcode v1.0.1 // indirect
8 | github.com/fatih/color v1.10.0 // indirect
9 | github.com/fsnotify/fsnotify v1.4.9 // indirect
10 | github.com/gin-gonic/gin v1.7.1
11 | github.com/githubnemo/CompileDaemon v1.2.1 // indirect
12 | github.com/hako/durafmt v0.0.0-20210316092057-3a2c319c1acd
13 | )
14 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "net/http"
6 | )
7 |
8 | func main() {
9 | r := gin.Default()
10 |
11 | r.GET("/", ping)
12 | v1 := r.Group("/v1")
13 | {
14 | v1.GET("/", ping)
15 | v1.GET("/ping", ping)
16 | v1.GET("/generator", generator)
17 | v1.GET("/validator", validator)
18 | }
19 |
20 | r.Run(":3000")
21 | }
22 |
23 | func ping(c *gin.Context) {
24 | c.JSON(http.StatusOK, gin.H{
25 | "message": "pong",
26 | })
27 | }
28 |
--------------------------------------------------------------------------------
/nic_validator.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/gin-gonic/gin"
6 | "github.com/hako/durafmt"
7 | "net/http"
8 | "strconv"
9 | "time"
10 | )
11 |
12 | func validator(c *gin.Context) {
13 | layout := "2006-01-02"
14 | dns := c.Query("nic") // Data NIC string
15 | val18 := c.Query("val18")
16 |
17 | if val18 != "false" && val18 != "0" {
18 | val18 = "true"
19 | }
20 |
21 | if len(dns) == 0 {
22 | sendErrorJsonValidator(c, fmt.Errorf("nic parameter does not exist."), http.StatusBadRequest)
23 | return
24 | }
25 |
26 | date, doy, age, err := dateHandler(dns)
27 |
28 | if err != nil {
29 | sendErrorJsonValidator(c, err, http.StatusBadRequest)
30 | return
31 | }
32 |
33 | version, err := versionCheck(dns)
34 |
35 | if err != nil {
36 | sendErrorJsonValidator(c, err, http.StatusBadRequest)
37 | return
38 | }
39 |
40 | sex, err := sexCheck(dns)
41 |
42 | if err != nil {
43 | sendErrorJsonValidator(c, err, http.StatusBadRequest)
44 | return
45 | }
46 |
47 | fage, err := durafmt.ParseString(age.String())
48 |
49 | if err != nil {
50 | sendErrorJsonValidator(c, err, http.StatusBadRequest)
51 | return
52 | }
53 |
54 | sn, err := serialNumberHandler(dns)
55 |
56 | if err != nil {
57 | sendErrorJsonValidator(c, err, http.StatusBadRequest)
58 | return
59 | }
60 |
61 | cd, err := checkDigitHandler(dns)
62 |
63 | if err != nil {
64 | sendErrorJsonValidator(c, err, http.StatusBadRequest)
65 | return
66 | }
67 |
68 | c.JSON(http.StatusOK, gin.H{
69 | "status": true,
70 | "date": date.Format(layout),
71 | "doy": doy,
72 | "age": fage.LimitFirstN(3).String(),
73 | "version": version,
74 | "sex": sex,
75 | "sn": gin.H{
76 | "old": fmt.Sprintf("%03d", sn),
77 | "new": fmt.Sprintf("%04d", sn),
78 | },
79 | "cd": cd,
80 | "validateStatus": true,
81 | })
82 |
83 | }
84 |
85 | func checkDigitHandler(dns string) (int, error) {
86 | if len(dns) == 10 {
87 | cd, errCheckDigitParse := strconv.ParseInt(string(dns[8]), 0, 64) // Check digit
88 |
89 | if errCheckDigitParse != nil {
90 | return 0, fmt.Errorf("Error occured in check digit parse.")
91 | }
92 |
93 | return int(cd), nil
94 | } else if len(dns) == 12 {
95 | cd, errCheckDigitParse := strconv.ParseInt(string(dns[len(dns)-1]), 0, 64) // Check digit
96 |
97 | if errCheckDigitParse != nil {
98 | return 0, fmt.Errorf("Error occured in check digit parse.")
99 | }
100 |
101 | return int(cd), nil
102 | } else {
103 | return 0, fmt.Errorf("Error occured on check digit handler.")
104 | }
105 | }
106 |
107 | func serialNumberHandler(dns string) (int, error) {
108 | if len(dns) == 10 {
109 | sn, errSerialNumberParse := strconv.ParseInt(dns[5:8], 10, 64) // Serial Number
110 |
111 | if errSerialNumberParse != nil {
112 | return 0, fmt.Errorf("Error occured in serial number parse.")
113 | }
114 |
115 | return int(sn), nil
116 | } else if len(dns) == 12 {
117 | sn, errSerialNumberParse := strconv.ParseInt(dns[7:11], 10, 64) // Serial Number
118 |
119 | if errSerialNumberParse != nil {
120 | return 0, fmt.Errorf("Error occured in serial number parse.")
121 | }
122 |
123 | return int(sn), nil
124 | } else {
125 | return 0, fmt.Errorf("Error occured on serial number handler.")
126 | }
127 | }
128 |
129 | func sexCheck(dns string) (string, error) {
130 | if len(dns) == 10 {
131 | sas := "Male"
132 |
133 | sosd, errSexParse := strconv.ParseInt(dns[2:5], 0, 64) // String of sex digits
134 | if errSexParse != nil {
135 | return "", fmt.Errorf("Error occured in day of the year parse.")
136 | }
137 |
138 | if sosd > 500 {
139 | sas = "Female"
140 | }
141 |
142 | return sas, nil
143 | } else if len(dns) == 12 {
144 | sas := "Male"
145 |
146 | sosd, errSexParse := strconv.ParseInt(dns[4:7], 0, 64) // String of sex digits
147 | if errSexParse != nil {
148 | return "", fmt.Errorf("Error occured in day of the year parse.")
149 | }
150 |
151 | if sosd > 500 {
152 | sas = "Female"
153 | }
154 |
155 | return sas, nil
156 | } else {
157 | return "", fmt.Errorf("Error occured on sex check.")
158 | }
159 | }
160 |
161 | func versionCheck(dns string) (string, error) {
162 | if len(dns) == 10 {
163 | return "Old", nil
164 | } else if len(dns) == 12 {
165 | return "New", nil
166 | } else {
167 | return "", fmt.Errorf("Error occured on version check.")
168 | }
169 | }
170 |
171 | func dateHandler(dns string) (time.Time, int, time.Duration, error) {
172 | layout := "2006-01-02"
173 |
174 | if len(dns) == 10 {
175 | year := fmt.Sprintf("19%v", dns[0:2])
176 | fdoy := fmt.Sprintf("%v-01-01", year)
177 | date, errTimeParse := time.Parse(layout, fdoy)
178 | if errTimeParse != nil {
179 | return time.Now(), 0, 0, fmt.Errorf("Error occured in year parse.")
180 | }
181 |
182 | doy, errIntParse := strconv.ParseInt(dns[2:5], 0, 64)
183 | if errIntParse != nil {
184 | return time.Now(), 0, 0, fmt.Errorf("Error occured in day of the year parse.")
185 | }
186 |
187 | if doy > 500 {
188 | doy -= 500
189 | }
190 | date = date.AddDate(0, 0, int(doy-1))
191 | age := time.Since(date)
192 |
193 | return date, int(doy), age, nil
194 | } else if len(dns) == 12 {
195 | year := dns[0:4]
196 | fdoy := fmt.Sprintf("%v-01-01", year)
197 | date, errTimeParse := time.Parse(layout, fdoy)
198 | if errTimeParse != nil {
199 | return time.Now(), 0, 0, fmt.Errorf("Error occured in year parse.")
200 | }
201 |
202 | doy, errIntParse := strconv.ParseInt(dns[4:7], 0, 64)
203 | if errIntParse != nil {
204 | return time.Now(), 0, 0, fmt.Errorf("Error occured in day of the year parse.")
205 | }
206 |
207 | if doy > 500 {
208 | doy -= 500
209 | }
210 | date = date.AddDate(0, 0, int(doy-1))
211 | age := time.Since(date)
212 |
213 | return date, int(doy), age, nil
214 | } else {
215 | return time.Now(), 0, 0, fmt.Errorf("nic parameter value is incorrect.")
216 | }
217 | }
218 |
219 | //func validate18Years(val18 string) {
220 | // //bval18 := true
221 | // //layout := "2006-01-02"
222 | // //
223 | // //if val18 == "false" || val18 == "0" {
224 | // // bval18 = false
225 | // //}
226 | //}
227 |
228 | func sendErrorJsonValidator(c *gin.Context, err error, code int) {
229 | c.JSON(code, gin.H{
230 | "status": false,
231 | "error": err.Error(),
232 | "code": http.StatusText(code),
233 | "validateStatus": false,
234 | })
235 | }
236 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sri Lanka NIC Generator and Validator
2 |
3 | This is a project to implement a Generator and Validator for Sri Lanka National Identity Card (NIC).
4 | This is currently an API only project build using a state-of-the-art programming language of [Go](https://golang.org/).
5 | This project also utilize Few Go packages including:
6 | * [Gin Web Framework](https://github.com/gin-gonic/) - For API implementation
7 | * [durafmt](https://github.com/hako/durafmt) - For getting duration between Times
8 | * [go-randomdata](https://github.com/Pallinder/go-randomdata) - For getting random Int, Boolean, Times
9 | * [barcode](https://github.com/boombuler/barcode) - For generating the PDF417 barcode that in the NIC version introduced in 2016
10 |
11 | This is currently ongoing project and hope to improve the functionality lot more.
12 |
13 | ## Usage
14 |
15 | ### NIC generator
16 |
17 | For generation of and NIC call ```/v1/generator``` endpoint. It has a few query params it could take
18 | to generate NIC number according to the parameters. These params includes the following:
19 | * sex - f, female, m , male
20 | * date - date of which NIC holder is born (Ex: 1995-05-17)
21 |
22 | #### Provinces
23 |
24 | API is returning a province which includes a number and a name. This province name or number does not
25 | relate to NIC number itself. This is given because Old NIC type had a number indicating the province in the
26 | physical NIC. List of the provinces, and the numbers as follows:
27 |
28 | 1. Western Province
29 | 2. Central Province
30 | 3. Southern Province
31 | 4. Northern Province
32 | 5. Eastern Province
33 | 6. North Western Province
34 | 7. North Central Province
35 | 8. Uva Province
36 | 9. Sabaragahmuwa Province
37 |
38 | #### Barcodes
39 |
40 | New NIC card has a barcode which contain the same data that written in the card itself.
41 | This barcode is type of PDF417 and can be scanned and get the data using simple barcode scanning app.
42 | I have implemented the generation of barcode as a base64 image that can be transferred in an API call.
43 | Generator end point response contain the barcode object for the content nad image of the generated data.
44 | Sample barcode image and data contain in that given below.
45 |
46 | ```
47 | 00
48 | 196165906402
49 | 08/06/1961
50 | Female
51 | 1972-05-29
52 | 00BFT-710
53 | Ava Martinez
54 | 92 Madison Pkwy,
55 | Northleach, NC, 35229
56 | Ransom Canyon
57 | 485406C0548FDD8FDDF300F312EE947D#
58 | ```
59 | 
60 |
61 | #### Examples
62 |
63 | ```
64 | // http://localhost:3000/v1/generator?sex=male
65 | // Usage of the sex param
66 |
67 | {
68 | "cd": 4,
69 | "date": "1948-12-23",
70 | "doy": 358,
71 | "nnic": "19483580944",
72 | "onic": "48358944V",
73 | "province": {
74 | "name": "Western Province",
75 | "number": 1
76 | },
77 | "sex": "Male",
78 | "sn": {
79 | "new": "0094",
80 | "old": "094"
81 | },
82 | "status": true,
83 | "barcode": {
84 | "content": "00\n199427605649\n03/10/1994\nMale\n2005-03-20.........",
85 | "image": "data:image/png;base64,iVBORw0...."
86 | }
87 | }
88 |
89 | // http://localhost:3000/v1/generator?date=1995-05-17
90 | // Usage of the date param
91 |
92 | {
93 | "cd": 4,
94 | "date": "1995-05-17",
95 | "doy": 137,
96 | "nnic": "199563701074",
97 | "onic": "956371074V",
98 | "province": {
99 | "name": "Southern Province",
100 | "number": 3
101 | },
102 | "sex": "Female",
103 | "sn": {
104 | "new": "0107",
105 | "old": "107"
106 | },
107 | "status": true,
108 | "barcode": {
109 | "content": "00\n199427605649\n03/10/1994\nMale\n2005-03-20.........",
110 | "image": "data:image/png;base64,iVBORw0...."
111 | }
112 | }
113 |
114 | // http://localhost:3000/v1/generator?date=1996-06-03&sex=m
115 | // Usage of the both sex and date param
116 |
117 | {
118 | "cd": 6,
119 | "date": "1996-06-03",
120 | "doy": 155,
121 | "nnic": "199615506166",
122 | "onic": "961556166V",
123 | "province": {
124 | "name": "Western Province",
125 | "number": 1
126 | },
127 | "sex": "Male",
128 | "sn": {
129 | "new": "0616",
130 | "old": "616"
131 | },
132 | "status": true,
133 | "barcode": {
134 | "content": "00\n199427605649\n03/10/1994\nMale\n2005-03-20.........",
135 | "image": "data:image/png;base64,iVBORw0...."
136 | }
137 | }
138 |
139 | // http://localhost:3000/v1/generator?date=2005-05-17
140 | // Usage of if birthday is in or after year 2000
141 | // Note that if the birthday is in or after year 2000,
142 | // response will not have onic (Old NIC) and Old serial number
143 |
144 | {
145 | "cd": 4,
146 | "date": "2005-05-17",
147 | "doy": 137,
148 | "nnic": "200563797534",
149 | "onic": "",
150 | "sex": "Female",
151 | "sn": {
152 | "new": "9753",
153 | "old": ""
154 | },
155 | "status": true,
156 | "barcode": {
157 | "content": "00\n199427605649\n03/10/1994\nMale\n2005-03-20.........",
158 | "image": "data:image/png;base64,iVBORw0...."
159 | }
160 | }
161 | ```
162 |
163 | ### NIC validator
164 | For validation of the NIC call ```/v1/validator``` endpoint. It must have a ```nic``` parameter.
165 | Otherwise it will return an error saying that ```nic``` parameter is empty. NIC that send in the parameter ```nic``` could be
166 | both new of old version of the NIC in sri lanka
167 |
168 | #### Examples
169 |
170 | ```
171 | // http://localhost:3000/v1/validator?nic=956380995V
172 | // Using old version of the NIC
173 |
174 | {
175 | "age": "25 years 47 weeks 6 days",
176 | "cd": 5,
177 | "date": "1995-05-18",
178 | "doy": 138,
179 | "sex": "Female",
180 | "status": true,
181 | "sn": {
182 | "new": "0099",
183 | "old": "099"
184 | },
185 | "validateStatus": true,
186 | "version": "Old"
187 | }
188 |
189 | // http://localhost:3000/v1/validator?nic=199615500343
190 | // Using new version of the NIC
191 |
192 | {
193 | "age": "24 years 45 weeks 3 days",
194 | "cd": 3,
195 | "date": "1996-06-03",
196 | "doy": 155,
197 | "sex": "Male",
198 | "sn": {
199 | "new": "0034",
200 | "old": "034"
201 | },
202 | "status": true,
203 | "validateStatus": true,
204 | "version": "New"
205 | }
206 |
207 | // http://localhost:3000/v1/validator?nic=9563809s95V
208 | // Response when NIC is not valid
209 |
210 | {
211 | "code": "Bad Request",
212 | "error": "nic parameter value is incorrect.",
213 | "status": false,
214 | "validateStatus": false
215 | }
216 |
217 | ```
218 |
219 | ## Do you like it? Star it!
220 | If you use this component just star it. A developer is more motivated to improve a project when there is some interest.
221 |
222 | ## Contributing
223 | Bug reports and pull requests are welcome on GitHub at https://github.com/randikabanura/sri-lanka-nic-generator-validator.
224 |
225 | ## License
226 | The software is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
227 |
228 | ## Developer
229 | Name: [Banura Randika Perera](https://github.com/randikabanura)
230 | Linkedin: [randika-banura](https://www.linkedin.com/in/randika-banura/)
231 | Email: [randika.banura@gamil.com](mailto:randika.banura@gamil.com)
232 | Bsc (Hons) Information Technology specialized in Software Engineering (SLIIT)
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/Pallinder/go-randomdata v1.2.0 h1:DZ41wBchNRb/0GfsePLiSwb0PHZmT67XY00lCDlaYPg=
2 | github.com/Pallinder/go-randomdata v1.2.0/go.mod h1:yHmJgulpD2Nfrm0cR9tI/+oAgRqCQQixsA8HyRZfV9Y=
3 | github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
4 | github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8 | github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
9 | github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
10 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
11 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
12 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
13 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
14 | github.com/gin-gonic/gin v1.7.1 h1:qC89GU3p8TvKWMAVhEpmpB2CIb1hnqt2UdKZaP93mS8=
15 | github.com/gin-gonic/gin v1.7.1/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
16 | github.com/githubnemo/CompileDaemon v1.2.1 h1:yEJ28U3sIsuhCtpqN2Pb5XL8G+hpg7KUNfTGvL7GxL0=
17 | github.com/githubnemo/CompileDaemon v1.2.1/go.mod h1:lE3EXX1td33uhlkFLp+ImWY9qBaoRcDeA3neh4m8ic0=
18 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
19 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
20 | github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
21 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
22 | github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
23 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
24 | github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
25 | github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
26 | github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
27 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
28 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
29 | github.com/hako/durafmt v0.0.0-20210316092057-3a2c319c1acd h1:FsX+T6wA8spPe4c1K9vi7T0LvNCO1TTqiL8u7Wok2hw=
30 | github.com/hako/durafmt v0.0.0-20210316092057-3a2c319c1acd/go.mod h1:VzxiSdG6j1pi7rwGm/xYI5RbtpBgM8sARDXlvEvxlu0=
31 | github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
32 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
33 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
34 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
35 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
36 | github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
37 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
38 | github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
39 | github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
40 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
41 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
42 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
43 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
44 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
45 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
46 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
47 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
48 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
49 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
50 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
51 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
52 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
53 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
54 | github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
55 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
56 | github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
57 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
58 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
59 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
60 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
61 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
62 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
63 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
64 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
65 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=
66 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
67 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
68 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
69 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
70 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
71 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
72 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
73 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
74 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
75 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
76 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
77 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
78 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
79 |
--------------------------------------------------------------------------------
/nic_generator.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/base64"
5 | "fmt"
6 | "github.com/Pallinder/go-randomdata"
7 | "github.com/boombuler/barcode/pdf417"
8 | "github.com/gin-gonic/gin"
9 | "image/png"
10 | "io/ioutil"
11 | "math"
12 | "net/http"
13 | "os"
14 | "strings"
15 | "time"
16 | )
17 |
18 | func generator(c *gin.Context) {
19 | layout := "2006-01-02"
20 | dqs := c.Query("date")
21 | provinces := []string{"Western", "Central", "Southern", "Northern", "Eastern", "North Western", "North Central", "Uva", "Sabaragahmuwa"}
22 |
23 | date, err := dateQueryHandler(dqs) // Date query string
24 |
25 | if err != nil {
26 | sendErrorJsonGenerator(c, err, http.StatusBadRequest)
27 | return
28 | }
29 |
30 | sqs := c.Query("sex") // Sex query string
31 | sex, sas, err := sexQueryHandler(sqs)
32 |
33 | if err != nil {
34 | sendErrorJsonGenerator(c, err, http.StatusBadRequest)
35 | return
36 | }
37 |
38 | fdoy := time.Date(date.Year(), 1, 1, 0, 0, 0, 0, time.UTC) // First day of the year
39 | doy := int(math.Ceil(date.Sub(fdoy).Hours()/24) + 1) // Day of the year
40 |
41 | sn := randomdata.Number(0, 1000) // Serial number
42 | if date.Year() >= 2000 {
43 | sn = randomdata.Number(0, 10000)
44 | }
45 |
46 | cd := randomdata.Number(0, 10) // Check digit
47 |
48 | sdoy := doy // Day of the year according to sex
49 | if sex == false {
50 | sdoy += 500
51 | }
52 |
53 | onic := generateONIC(date.Year(), sdoy, sn, cd)
54 | osn := fmt.Sprintf("%03d", sn) // Old serial number
55 | if len(onic) != 10 {
56 | onic = ""
57 | osn = ""
58 | }
59 |
60 | nnic := generateNNIC(date.Year(), sdoy, sn, cd)
61 | nsn := fmt.Sprintf("%04d", sn) // New serial number
62 | barcode := ""
63 | barcodeContent := ""
64 | if len(nnic) != 12 {
65 | nnic = ""
66 | nsn = ""
67 | } else {
68 | barcodeContent, barcode, err = generateBarcodeForNNIC(nnic, date, sas)
69 | if err != nil {
70 | sendErrorJsonGenerator(c, err, http.StatusBadRequest)
71 | return
72 | }
73 | }
74 |
75 | pn := randomdata.Number(0, 9) // Province number. This is not associated with the NIC number
76 | ps := fmt.Sprintf("%v Province", provinces[pn]) // String of province
77 |
78 | c.JSON(http.StatusOK, gin.H{
79 | "status": true,
80 | "date": date.Format(layout),
81 | "doy": doy,
82 | "sn": gin.H{
83 | "old": osn, // Old serial number
84 | "new": nsn, // New serial number
85 | },
86 | "cd": cd,
87 | "sex": sas,
88 | "onic": onic, // Old nic version
89 | "nnic": nnic, // New nic version
90 | "province": gin.H{ // Province is not associated with the NIC number
91 | "number": pn + 1,
92 | "name": ps,
93 | },
94 | "barcode": gin.H{
95 | "content": barcodeContent,
96 | "image": barcode,
97 | },
98 | })
99 | }
100 |
101 | // Handle error response if any error occurred for generator
102 | func sendErrorJsonGenerator(c *gin.Context, err error, code int) {
103 | c.JSON(code, gin.H{
104 | "status": false,
105 | "error": err.Error(),
106 | "code": http.StatusText(code),
107 | })
108 | }
109 |
110 | // Handles a date query param. If not available it auto generate random date
111 | func dateQueryHandler(dqs string) (time.Time, error) {
112 | layout := "2006-01-02"
113 | date := time.Now()
114 | var err error = nil
115 |
116 | if len(dqs) > 0 {
117 | date, err = time.Parse(layout, dqs)
118 | } else {
119 | db18 := time.Now().AddDate(-18, 0, 0).Format(layout) // Date 18 years before today
120 | db118 := time.Now().AddDate(-118, 0, 0).Format(layout) // Date 118 years before today
121 | date, err = time.Parse("Monday 2 Jan 2006", randomdata.FullDateInRange(db118, db18))
122 | }
123 |
124 | return date, err
125 | }
126 |
127 | // Handles the sex query param and return a boolean, string and error
128 | func sexQueryHandler(sqs string) (bool, string, error) {
129 | sqs = strings.ToLower(sqs)
130 |
131 | switch sqs {
132 | case "m", "male":
133 | {
134 | return true, "Male", nil
135 | }
136 | case "f", "female":
137 | {
138 | return false, "Female", nil
139 | }
140 | case "":
141 | {
142 | rs := randomdata.Boolean() // Random sex boolean
143 | rss := "Male" // initialize the the sex string
144 |
145 | // Change the sex string if rs is false
146 | if rs == false {
147 | rss = "Female"
148 | }
149 | return rs, rss, nil
150 | }
151 | default:
152 | return false, "", fmt.Errorf("Sex parameter can not be parsed.")
153 | }
154 | }
155 |
156 | // Generate old nic version according to year, day of the year, serial number and check digit
157 | func generateONIC(year int, doy int, sn int, cd int) string {
158 | if sn > 999 || year > 2000 {
159 | return ""
160 | }
161 |
162 | sy := year % 100
163 | ssy := fmt.Sprintf("%v", sy)
164 |
165 | if sy < 10 {
166 | ssy = fmt.Sprintf("0%v", sy)
167 | }
168 |
169 | return fmt.Sprintf("%v%03d%03d%d%v", ssy, doy, sn, cd, "V")
170 | }
171 |
172 | // Generate new nic version according to year, day of the year, serial number and check digit
173 | func generateNNIC(year int, doy int, sn int, cd int) string {
174 | return fmt.Sprintf("%d%03d%04d%d", year, doy, sn, cd)
175 | }
176 |
177 | // Generate the pdf417 barcode for the new NIC number
178 | func generateBarcodeForNNIC(nnic string, date time.Time, sas string) (string, string, error) {
179 | layoutOne := "2006-01-02"
180 | layoutTwo := "02/01/2006"
181 | fullName := ""
182 | if sas == "Male" {
183 | fullName = randomdata.FullName(randomdata.Male)
184 | } else {
185 | fullName = randomdata.FullName(randomdata.Female)
186 | }
187 |
188 | db := date.Format(layoutOne)
189 | db16 := date.AddDate(16, 0, 0).Format(layoutOne) // Date 16 years after today
190 | createdDate, err := time.Parse("Monday 2 Jan 2006", randomdata.FullDateInRange(db, db16))
191 | if err != nil {
192 | return "", "", err
193 | }
194 |
195 | barcodeStr := "00\n" +
196 | nnic + "\n" +
197 | date.Format(layoutTwo) + "\n" +
198 | sas + "\n" +
199 | createdDate.Format(layoutOne) + "\n" +
200 | "00BFT-710\n" +
201 | fullName + "\n" +
202 | randomdata.Address() + "\n" +
203 | randomdata.City() + "\n" +
204 | "485406C0548FDD8FDDF300F312EE947D#"
205 |
206 | currentTime := time.Now().Unix()
207 |
208 | pdf417Code, err := pdf417.Encode(barcodeStr, 0)
209 | if err != nil {
210 | return "", "", err
211 | }
212 |
213 | // create the output file
214 | file, err := os.Create(fmt.Sprintf("barcode-%v.png", currentTime))
215 | if err != nil {
216 | return "", "", err
217 | }
218 |
219 | defer file.Close()
220 |
221 | err = png.Encode(file, pdf417Code)
222 | if err != nil {
223 | return "", "", err
224 | }
225 |
226 | bytes, err := ioutil.ReadFile(fmt.Sprintf("./barcode-%v.png", currentTime))
227 | if err != nil {
228 | return "", "", err
229 | }
230 |
231 | err = os.Remove(fmt.Sprintf("barcode-%v.png", currentTime))
232 | if err != nil {
233 | return "", "", err
234 | }
235 |
236 | mimeType := http.DetectContentType(bytes)
237 |
238 | // Prepend the appropriate URI scheme header depending
239 | // on the MIME type
240 | base64Encoding := getMimeType(mimeType)
241 |
242 | // Append the base64 encoded output
243 | base64Encoding += toBase64(bytes)
244 |
245 | // Print the full base64 representation of the image
246 | return pdf417Code.Content(), base64Encoding, nil
247 | }
248 |
249 | func toBase64(b []byte) string {
250 | return base64.StdEncoding.EncodeToString(b)
251 | }
252 |
253 | func getMimeType(mimetype string) string {
254 | base64Encoding := ""
255 |
256 | switch mimetype {
257 | case "image/jpeg":
258 | base64Encoding = "data:image/jpeg;base64,"
259 | case "image/png":
260 | base64Encoding = "data:image/png;base64,"
261 | }
262 |
263 | return base64Encoding
264 | }
265 |
--------------------------------------------------------------------------------